<lambda>null1 package leakcanary.internal.navigation
2 
3 import android.app.Activity
4 import android.content.Intent
5 import android.os.Build.VERSION.SDK_INT
6 import android.os.Bundle
7 import android.os.Parcelable
8 import android.view.Menu
9 import android.view.MenuItem
10 import android.view.View
11 import android.view.ViewGroup
12 import android.view.animation.AnimationUtils.loadAnimation
13 import com.squareup.leakcanary.core.R
14 
15 /**
16  * A simple backstack navigating activity
17  */
18 internal abstract class NavigatingActivity : Activity() {
19 
20   private lateinit var backstack: ArrayList<BackstackFrame>
21   private lateinit var currentScreen: Screen
22 
23   private lateinit var container: ViewGroup
24   private lateinit var currentView: View
25 
26   var onCreateOptionsMenu = NO_MENU
27 
28   fun installNavigation(
29     savedInstanceState: Bundle?,
30     container: ViewGroup
31   ) {
32     this.container = container
33 
34     if (savedInstanceState == null) {
35       backstack = ArrayList()
36       val screens = parseIntentScreens(intent)
37       currentScreen = if (screens.isNotEmpty()) {
38         screens.dropLast(1)
39           .forEach { screen ->
40             backstack.add(BackstackFrame(screen))
41           }
42         screens.last()
43       } else {
44         getLauncherScreen()
45       }
46     } else {
47       currentScreen = savedInstanceState.getSerializable("currentScreen") as Screen
48       @Suppress("UNCHECKED_CAST")
49       backstack = savedInstanceState.getParcelableArrayList<Parcelable>(
50         "backstack"
51       ) as ArrayList<BackstackFrame>
52     }
53     currentView = currentScreen.createView(container)
54     container.addView(currentView)
55 
56     actionBar?.run {
57       setHomeButtonEnabled(true)
58       setDisplayHomeAsUpEnabled(true)
59     }
60     screenUpdated()
61   }
62 
63   override fun onNewIntent(intent: Intent) {
64     val screens = parseIntentScreens(intent)
65     if (screens.isNotEmpty()) {
66       backstack.clear()
67       screens.dropLast(1)
68         .forEach { screen ->
69           backstack.add(BackstackFrame(screen))
70         }
71       goTo(screens.last())
72     }
73   }
74 
75   abstract fun parseIntentScreens(intent: Intent): List<Screen>
76 
77   open fun getLauncherScreen(): Screen {
78     TODO("Launcher activities should override getLauncherScreen()")
79   }
80 
81   public override fun onSaveInstanceState(outState: Bundle) {
82     super.onSaveInstanceState(outState)
83     outState.putSerializable("currentScreen", currentScreen)
84     outState.putParcelableArrayList("backstack", backstack)
85   }
86 
87   override fun onBackPressed() {
88     if (backstack.size > 0) {
89       goBack()
90       return
91     }
92     super.onBackPressed()
93   }
94 
95   fun resetTo(screen: Screen) {
96     onCreateOptionsMenu = NO_MENU
97 
98     currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_exit_alpha))
99     container.removeView(currentView)
100     currentView.notifyScreenExiting()
101 
102     backstack.clear()
103 
104     currentScreen = screen
105     currentView = currentScreen.createView(container)
106     currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_enter_alpha))
107     container.addView(currentView)
108 
109     screenUpdated()
110   }
111 
112   fun goTo(screen: Screen) {
113     onCreateOptionsMenu = NO_MENU
114 
115     currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_exit_forward))
116     container.removeView(currentView)
117     currentView.notifyScreenExiting()
118     val backstackFrame = BackstackFrame(currentScreen, currentView)
119     backstack.add(backstackFrame)
120 
121     currentScreen = screen
122     currentView = currentScreen.createView(container)
123     currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_enter_forward))
124     container.addView(currentView)
125 
126     screenUpdated()
127   }
128 
129   fun refreshCurrentScreen() {
130     onCreateOptionsMenu = NO_MENU
131     container.removeView(currentView)
132     currentView.notifyScreenExiting()
133     currentView = currentScreen.createView(container)
134     container.addView(currentView)
135 
136     screenUpdated()
137   }
138 
139   fun goBack() {
140     onCreateOptionsMenu = NO_MENU
141 
142     currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_exit_backward))
143     container.removeView(currentView)
144     currentView.notifyScreenExiting()
145 
146     val latest = backstack.removeAt(backstack.size - 1)
147     currentScreen = latest.screen
148     currentView = currentScreen.createView(container)
149     currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_enter_backward))
150     container.addView(currentView, 0)
151     latest.restore(currentView)
152 
153     screenUpdated()
154   }
155 
156   private fun screenUpdated() {
157     invalidateOptionsMenu()
158     if (SDK_INT >= 18) {
159       actionBar?.run {
160         val goBack = backstack.size > 0
161         val indicator = if (goBack) 0 else android.R.drawable.ic_menu_close_clear_cancel
162         setHomeAsUpIndicator(indicator)
163       }
164     }
165     onNewScreen(currentScreen)
166   }
167 
168   protected open fun onNewScreen(screen: Screen) {
169   }
170 
171   override fun onCreateOptionsMenu(menu: Menu): Boolean {
172     onCreateOptionsMenu.invoke(menu)
173     return true
174   }
175 
176   override fun onOptionsItemSelected(item: MenuItem): Boolean =
177     when (item.itemId) {
178       android.R.id.home -> {
179         onBackPressed()
180         true
181       }
182       else -> super.onOptionsItemSelected(item)
183     }
184 
185   override fun onDestroy() {
186     super.onDestroy()
187     currentView.notifyScreenExiting()
188   }
189 
190   companion object {
191     val NO_MENU: ((Menu) -> Unit) = {}
192   }
193 }
194