Slide 1

Slide 1 text

@cyrilmottier Launch screens From a Tap to your App

Slide 2

Slide 2 text

38 sec average interaction time

Slide 3

Slide 3 text

23 % apps used only once 38 sec average interaction time

Slide 4

Slide 4 text

13 launches per month 23 % apps used only once 38 sec average interaction time Source: Mobile Life Research Centre, University of Stockholm & Localytics

Slide 5

Slide 5 text

Polish your launching experience

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Placeholder UI /ˈpleɪshəʊldə(r) juː-aɪ/ Display core structural elements (status bar, app bar, etc.) without content until the app has loaded.

Slide 8

Slide 8 text

Branded launch /ˈbrændɪd lɔːntʃ/ Display your logo or other elements that improve brand recognition to portray a brand while the app is loading.

Slide 9

Slide 9 text

Branded launch Google Maps Placeholder UI Settings

Slide 10

Slide 10 text

Branded launch Google Maps Placeholder UI Settings your side Choose

Slide 11

Slide 11 text

Branded launch Google Maps Placeholder UI Settings

Slide 12

Slide 12 text

Yes! A launch screen is intentionally boring

Slide 13

Slide 13 text

Source: iOS Human Interface Guidelines, Apple If you think that following these guidelines will result in a plain, boring launch image, you’re right. Remember, the launch image doesn’t provide you with an opportunity for artistic expression. It’s solely intended to enhance the user’s perception of your app as quick to launch and immediately ready for use. “

Slide 14

Slide 14 text

Timeline of a launch

Slide 15

Slide 15 text

Timeline of a launch Slowed down version

Slide 16

Slide 16 text

User taps on icon Timeline of a launch 1 2 3 4 5

Slide 17

Slide 17 text

User taps on icon Timeline of a launch 1 2 Fit to screen animation starts 3 4 5

Slide 18

Slide 18 text

User taps on icon Timeline of a launch 1 2 Fit to screen animation starts 3 4 5 Fit to screen animation ends

Slide 19

Slide 19 text

User taps on icon Timeline of a launch 1 2 Fit to screen animation starts 3 4 5 Fit to screen animation ends Window switching starts

Slide 20

Slide 20 text

User taps on icon Timeline of a launch 1 2 Fit to screen animation starts 3 4 5 Fit to screen animation ends Window switching starts App content finishes loading

Slide 21

Slide 21 text

User taps on icon Timeline of a launch 1 2 Fit to screen animation starts 3 4 5 Fit to screen animation ends Window switching starts App content finishes loading

Slide 22

Slide 22 text

1 2 3 4 5 Launcher & Intent triggering Entering animation Static launch screen Content switching / fading Application fully drawn 1 2 3 4 5

Slide 23

Slide 23 text

1 2 3 5 System App Launcher & Intent triggering Entering animation Static launch screen Content switching / fading Application fully drawn 1 2 3 4 5 4

Slide 24

Slide 24 text

5 The perfect launch 1 2 3 4

Slide 25

Slide 25 text

1 Load and launch the app 2 Display a starting/preview window 3 Create the app process 4 Create the app object 5 Launch the main thread 6 Create the main activity 7 Inflate views 8 Lay out the screen 9 Perform the initial draw

Slide 26

Slide 26 text

1 Load and launch the app 2 Display a starting/preview window 3 Create the app process 4 Create the app object 5 Launch the main thread 6 Create the main activity 7 Inflate views 8 Lay out the screen 9 Perform the initial draw

Slide 27

Slide 27 text

System 1 Load and launch the app 2 Display a starting/preview window 3 Create the app process 4 Create the app object 5 Launch the main thread 6 Create the main activity 7 Inflate views 8 Lay out the screen 9 Perform the initial draw

Slide 28

Slide 28 text

System App 1 Load and launch the app 2 Display a starting/preview window 3 Create the app process 4 Create the app object 5 Launch the main thread 6 Create the main activity 7 Inflate views 8 Lay out the screen 9 Perform the initial draw

Slide 29

Slide 29 text

System App Cold start 1 Load and launch the app 2 Display a starting/preview window 3 Create the app process 4 Create the app object 5 Launch the main thread 6 Create the main activity 7 Inflate views 8 Lay out the screen 9 Perform the initial draw

Slide 30

Slide 30 text

System App Warm start 1 Load and launch the app 2 Display a starting/preview window 3 Create the app process 4 Create the app object 5 Launch the main thread 6 Create the main activity 7 Inflate views 8 Lay out the screen 9 Perform the initial draw

Slide 31

Slide 31 text

PhoneWindowManager.java Context context = mContext; try { context = context.createPackageContext(packageName, 0); context.setTheme(theme); } catch (PackageManager.NameNotFoundException e) { // Ignore } final PhoneWindow win = new PhoneWindow(context); win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); win.setFlags(/*...*/); final WindowManager.LayoutParams params = win.getAttributes(); // Tweak params ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)). addView(win.getDecorView(), params);

Slide 32

Slide 32 text

PhoneWindowManager.java Context context = mContext; try { context = context.createPackageContext(packageName, 0); context.setTheme(theme); } catch (PackageManager.NameNotFoundException e) { // Ignore } final PhoneWindow win = new PhoneWindow(context); win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); win.setFlags(/*...*/); final WindowManager.LayoutParams params = win.getAttributes(); // Tweak params ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)). addView(win.getDecorView(), params); Create Context for the started application context = context.createPackageContext(packageName, 0);

Slide 33

Slide 33 text

PhoneWindowManager.java Context context = mContext; try { context = context.createPackageContext(packageName, 0); context.setTheme(theme); } catch (PackageManager.NameNotFoundException e) { // Ignore } final PhoneWindow win = new PhoneWindow(context); win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); win.setFlags(/*...*/); final WindowManager.LayoutParams params = win.getAttributes(); // Tweak params ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)). addView(win.getDecorView(), params); Set the target theme context.setTheme(theme);

Slide 34

Slide 34 text

PhoneWindowManager.java Context context = mContext; try { context = context.createPackageContext(packageName, 0); context.setTheme(theme); } catch (PackageManager.NameNotFoundException e) { // Ignore } final PhoneWindow win = new PhoneWindow(context); win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); win.setFlags(/*...*/); final WindowManager.LayoutParams params = win.getAttributes(); // Tweak params ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)). addView(win.getDecorView(), params); final PhoneWindow win = new PhoneWindow(context); win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); win.setFlags(/*...*/); Create new Window and set it up final WindowManager.LayoutParams params = win.getAttributes(); // Tweak params

Slide 35

Slide 35 text

PhoneWindowManager.java Context context = mContext; try { context = context.createPackageContext(packageName, 0); context.setTheme(theme); } catch (PackageManager.NameNotFoundException e) { // Ignore } final PhoneWindow win = new PhoneWindow(context); win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); win.setFlags(/*...*/); final WindowManager.LayoutParams params = win.getAttributes(); // Tweak params ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)). addView(win.getDecorView(), params); ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)). addView(win.getDecorView(), params); Display the newly created Window

Slide 36

Slide 36 text

Same look, different process …and that explains a lot

Slide 37

Slide 37 text

$ adb logcat | grep “ActivityManager” ActivityManager: Displayed com.launchtime/.LaunchTimeActivity: +666ms API 19+

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

public class LaunchTimeActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks {
 
 private static final int LOADER_YO = 0xcafe;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_launch_time); 
 getSupportLoaderManager().initLoader(LOADER_YO, null, this);
 }
 
 @Override
 public void onLoadFinished(Loader loader, Void data) {
 switch (loader.getId()) {
 case LOADER_YO:
 // ...
 break;
 }
 reportFullyDrawn();
 }
 // ... }

Slide 40

Slide 40 text

public class LaunchTimeActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks {
 
 private static final int LOADER_YO = 0xcafe;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_launch_time); 
 getSupportLoaderManager().initLoader(LOADER_YO, null, this);
 }
 
 @Override
 public void onLoadFinished(Loader loader, Void data) {
 switch (loader.getId()) {
 case LOADER_YO:
 // ...
 break;
 }
 reportFullyDrawn();
 }
 // ... } reportFullyDrawn();

Slide 41

Slide 41 text

public class LaunchTimeActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks {
 
 private static final int LOADER_YO = 0xcafe;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_launch_time); 
 getSupportLoaderManager().initLoader(LOADER_YO, null, this);
 }
 
 @Override
 public void onLoadFinished(Loader loader, Void data) {
 switch (loader.getId()) {
 case LOADER_YO:
 // ...
 break;
 }
 reportFullyDrawn();
 }
 // ... } SecurityException on KitKat… reportFullyDrawn();

Slide 42

Slide 42 text

public final class ActivityCompatAdditions {
 
 private static final Impl IMPL;
 
 static {
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 IMPL = new LollipopImpl();
 } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
 IMPL = new KitKatImpl();
 } else {
 IMPL = new BaseImpl();
 }
 }
 
 private ActivityCompatAdditions() {
 // No instances
 } 
 
 public static void reportFullyDrawn(Activity activity) {
 IMPL.reportFullyDrawn(activity);
 } 
 
 private interface Impl {
 void reportFullyDrawn(Activity activity);
 } 


Slide 43

Slide 43 text

private static final class BaseImpl implements Impl {
 @Override
 public void reportFullyDrawn(Activity activity) {
 // No-op
 }
 }
 
 private static final class KitKatImpl implements Impl {
 @Override @TargetApi(Build.VERSION_CODES.KITKAT)
 public void reportFullyDrawn(Activity activity) {
 try {
 activity.reportFullyDrawn();
 } catch (SecurityException se) {
 // Prevent an Exception on KitKat
 }
 }
 }
 
 private static final class LollipopImpl implements Impl {
 @Override @TargetApi(Build.VERSION_CODES.KITKAT)
 public void reportFullyDrawn(Activity activity) {
 activity.reportFullyDrawn();
 }
 }
 
 }

Slide 44

Slide 44 text

public class LaunchTimeActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks {
 
 private static final int LOADER_YO = 0xcafe;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_launch_time); 
 getSupportLoaderManager().initLoader(LOADER_YO, null, this);
 }
 
 @Override
 public void onLoadFinished(Loader loader, Void data) {
 switch (loader.getId()) {
 case LOADER_YO:
 // ...
 break;
 }
 
 }
 // ... } reportFullyDrawn();

Slide 45

Slide 45 text

public class LaunchTimeActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks {
 
 private static final int LOADER_YO = 0xcafe;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_launch_time); 
 getSupportLoaderManager().initLoader(LOADER_YO, null, this);
 }
 
 @Override
 public void onLoadFinished(Loader loader, Void data) {
 switch (loader.getId()) {
 case LOADER_YO:
 // ...
 break;
 }
 
 }
 // ... } reportFullyDrawn();

Slide 46

Slide 46 text

public class LaunchTimeActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks {
 
 private static final int LOADER_YO = 0xcafe;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_launch_time); 
 getSupportLoaderManager().initLoader(LOADER_YO, null, this);
 }
 
 @Override
 public void onLoadFinished(Loader loader, Void data) {
 switch (loader.getId()) {
 case LOADER_YO:
 // ...
 break;
 }
 }
 // ... } ActivityCompatAdditions.reportFullyDrawn(this);

Slide 47

Slide 47 text

$ adb logcat | grep “ActivityManager” ActivityManager: Displayed com.launchtime/.LaunchTimeActivity: +666ms ActivityManager: Fully drawn com.launchtime/.LaunchTimeActivity: +1s440ms API 19+

Slide 48

Slide 48 text

com.launchtime/.LaunchScreenActivity $ adb shell am start

Slide 49

Slide 49 text

com.launchtime/.LaunchScreenActivity $ adb shell am start -W

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

$ adb shell am start -W com.launchtime/.LaunchScreenActivity Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.launchtime/.LaunchScreenActivity } Status: ok Activity: com.launchtime/.LaunchScreenActivity ThisTime: 2426 TotalTime: 2426 WaitTime: 2488 Complete

Slide 53

Slide 53 text

$ adb shell screenrecord /sdcard/launch.mp4 API 19+

Slide 54

Slide 54 text

$ adb shell screenrecord /sdcard/launch.mp4 --bugreport API 21+

Slide 55

Slide 55 text

API 21+

Slide 56

Slide 56 text

API 21+

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

11:16:46.666 f=26 (0)

Slide 59

Slide 59 text

11:16:46.666 f=26 (0) 11:16:46.666 Frame timestamp

Slide 60

Slide 60 text

11:16:46.666 f=26 (0) f=26 Frame number

Slide 61

Slide 61 text

11:16:46.666 f=26 (0) (0) Dropped frames count

Slide 62

Slide 62 text

11:16:46.666 f=26 (0) Frame timestamp Dropped frames count Frame number

Slide 63

Slide 63 text

SystemClock.sleep(2000L) all the things \o/ in debug only…

Slide 64

Slide 64 text

SystemClock.sleep(2000L) all the things \o/ in debug only…

Slide 65

Slide 65 text

com.launchtime/.LaunchScreenActivity $ adb shell am start

Slide 66

Slide 66 text

com.launchtime/.LaunchScreenActivity $ adb shell am start -S

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

Developer options Background process limit

Slide 70

Slide 70 text

Developer options Background process limit

Slide 71

Slide 71 text

Force cold launch with “No background processes”

Slide 72

Slide 72 text

Measure, measure and measure again!

Slide 73

Slide 73 text

Use Systrace & Traceview extensively

Slide 74

Slide 74 text

Lazy-load & async all the things

Slide 75

Slide 75 text

In general, beware of static init & onCreate

Slide 76

Slide 76 text

public class LazyProvider extends ContentProvider {
 
 private static final int MODEL = 101;
 private static final int MODEL_ID = 102;
 
 private static final UriMatcher URI_MATCHER;
 static {
 URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
 URI_MATCHER.addURI(LazyContract.AUTHORITY, "/model", MODEL);
 URI_MATCHER.addURI(LazyContract.AUTHORITY, "/model/#", MODEL_ID);
 // ...
 }
 
 private SQLiteOpenHelper mOpenHelper;
 
 @Override
 public boolean onCreate() {
 mOpenHelper = new LoadOpenHelper(getContext());
 return true;
 }
 // ...
 } LazyProvider.java

Slide 77

Slide 77 text

public class LazyProvider extends ContentProvider {
 
 private static final int MODEL = 101;
 private static final int MODEL_ID = 102;
 
 private static final UriMatcher URI_MATCHER;
 static {
 URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
 URI_MATCHER.addURI(LazyContract.AUTHORITY, "/model", MODEL);
 URI_MATCHER.addURI(LazyContract.AUTHORITY, "/model/#", MODEL_ID);
 // ...
 }
 
 private SQLiteOpenHelper mOpenHelper;
 
 @Override
 public boolean onCreate() {
 mOpenHelper = new LoadOpenHelper(getContext());
 return true;
 }
 // ...
 } LazyProvider.java private static final UriMatcher URI_MATCHER; Created at class loading but used only in CRUD…

Slide 78

Slide 78 text

public abstract class LazyGetter {
 
 private final AtomicReference> mCachedValue = new AtomicReference<>(Optional.empty());
 
 public final T get() {
 Optional value = mCachedValue.get();
 if (!value.isPresent()) {
 synchronized (mCachedValue) {
 value = mCachedValue.get();
 if (!value.isPresent()) {
 mCachedValue.set(Optional.ofNullable(initialValue()));
 }
 }
 }
 return mCachedValue.get().get();
 }
 
 protected abstract T initialValue();
 
 } LazyGetter.java

Slide 79

Slide 79 text

public class LazyProvider extends ContentProvider {
 
 private static final int MODEL = 101;
 private static final int MODEL_ID = 102;
 
 private static final LazyGetter URI_MATCHER = new LazyGetter() {
 @Override
 protected UriMatcher initialValue() {
 final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 uriMatcher.addURI(LazyContract.AUTHORITY, "/model", MODEL);
 uriMatcher.addURI(LazyContract.AUTHORITY, "/model/#", MODEL_ID);
 // ...
 return uriMatcher;
 }
 }; 
 // ...
 } LazyProvider.java

Slide 80

Slide 80 text

android:windowDisablePreview the (wrong) simple way…

Slide 81

Slide 81 text


 
 
 
 
 
 
 
 
 
 
 
 
 
 AndroidManifest.xml

Slide 82

Slide 82 text


 
 
 
 
 
 
 
 
 
 
 
 
 
 Default theme applies to all Activities AndroidManifest.xml android:theme="@style/Theme.LaunchTime">

Slide 83

Slide 83 text


 
 
 <item name="android:colorPrimary">@color/colorPrimary</item>
 <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
 <item name="android:colorAccent">@color/colorAccent</item>
 <item name="android:windowBackground">@color/appBackground</item>
 
 
 
 values/themes.xml

Slide 84

Slide 84 text

No project on Earth is minSdk 21+ except Android built-in apps!

Slide 85

Slide 85 text

to the rescue… AppCompat

Slide 86

Slide 86 text

to the rescue… AppCompat (almost)

Slide 87

Slide 87 text

to the rescue… AppCompat (almost)

Slide 88

Slide 88 text

UI design is all about

Slide 89

Slide 89 text

mirrors UI design is all about &mirrors mirrors & smoke

Slide 90

Slide 90 text

Switch between compile time & runtime themes

Slide 91

Slide 91 text


 
 
 
 
 values/attrs.xml

Slide 92

Slide 92 text


 
 
 
 <item name=“android:actionBarSize"> @dimen/abc_action_bar_default_height_material </item>
 <item name="android:actionBarStyle">@style/PreviewWidget.ActionBar</item> <item name="android:windowBackground">@color/appBackground</item> <item name=“ltRuntimeTheme">@style/Theme.LaunchTime</item> 
 
 
 values/themes_preview.xml

Slide 93

Slide 93 text


 
 
 
 <item name=“android:actionBarSize"> @dimen/abc_action_bar_default_height_material </item>
 <item name="android:actionBarStyle">@style/PreviewWidget.ActionBar</item> <item name="android:windowBackground">@color/appBackground</item> <item name=“ltRuntimeTheme">@style/Theme.LaunchTime</item> 
 
 
 values/themes_preview.xml @dimen/abc_action_bar_default_height_material Force pre-Lollipop ActionBars height to 48 / 56 / 64dp

Slide 94

Slide 94 text


 
 
 
 <item name=“android:actionBarSize"> @dimen/abc_action_bar_default_height_material </item>
 <item name="android:actionBarStyle">@style/PreviewWidget.ActionBar</item> <item name="android:windowBackground">@color/appBackground</item> <item name=“ltRuntimeTheme">@style/Theme.LaunchTime</item> 
 
 
 values/themes_preview.xml @style/Theme.LaunchTime Define the theme to use at runtime

Slide 95

Slide 95 text


 
 
 
 <item name="android:background">@color/colorPrimary</item>
 <item name=“android:displayOptions">showTitle</item> 
 
 
 values/styles_preview.xml

Slide 96

Slide 96 text


 
 
 
 <item name="android:colorPrimary">@color/colorPrimary</item>
 <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
 <item name="android:colorAccent">@color/colorAccent</item>
 <item name="android:windowBackground">@color/appBackground</item>
 <item name=“ltRuntimeTheme">@style/Theme.LaunchTime</item> 
 
 
 values-v21/themes_preview.xml

Slide 97

Slide 97 text


 
 
 
 <item name="android:colorPrimary">@color/colorPrimary</item>
 <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
 <item name="android:colorAccent">@color/colorAccent</item>
 <item name="android:windowBackground">@color/appBackground</item>
 <item name=“ltRuntimeTheme">@style/Theme.LaunchTime</item> 
 
 
 values-v21/themes_preview.xml parent=“android:Theme.Material.Light.DarkActionBar" Use Theme.Material on v-21+ devices

Slide 98

Slide 98 text


 
 
 
 <item name="android:colorPrimary">@color/colorPrimary</item>
 <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
 <item name="android:colorAccent">@color/colorAccent</item>
 <item name="android:windowBackground">@color/appBackground</item>
 <item name=“ltRuntimeTheme">@style/Theme.LaunchTime</item> 
 
 
 values-v21/themes_preview.xml Define the theme to use at runtime @style/Theme.LaunchTime

Slide 99

Slide 99 text


 
 
 
 
 
 
 
 
 
 
 
 
 
 AndroidManifest.xml

Slide 100

Slide 100 text


 
 
 
 
 
 
 
 
 
 
 
 
 
 AndroidManifest.xml android:theme="@style/PreviewTheme.LaunchTime" Tell the system to use a temporary preview theme

Slide 101

Slide 101 text

public final class ThemeUtils { private ThemeUtils() { // No instances } @UiThread public static void ensureRuntimeTheme(Context context) { final TypedValue tv = new TypedValue(); context.getTheme().resolveAttribute(R.attr.ltRuntimeTheme, tv, true); if (tv.resourceId <= 0) { throw new IllegalArgumentException("ltRuntimeTheme " + "not defined in the preview theme"); } context.setTheme(tv.resourceId); } } ThemeUtils.java

Slide 102

Slide 102 text

public class LaunchScreenActivity extends AppCompatActivity {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 ThemeUtils.ensureRuntimeTheme(this);
 super.onCreate(savedInstanceState); 
 setContentView(R.layout.activity_launch_screen);
 // ...
 }
 
 } LaunchScreenActivity.java

Slide 103

Slide 103 text

public class LaunchScreenActivity extends AppCompatActivity {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 ThemeUtils.ensureRuntimeTheme(this);
 super.onCreate(savedInstanceState); 
 setContentView(R.layout.activity_launch_screen);
 // ...
 }
 
 } LaunchScreenActivity.java Inherit AppCompatActivity to get AppCompat behaviour public class LaunchScreenActivity extends AppCompatActivity {

Slide 104

Slide 104 text

public class LaunchScreenActivity extends AppCompatActivity {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 ThemeUtils.ensureRuntimeTheme(this);
 super.onCreate(savedInstanceState); 
 setContentView(R.layout.activity_launch_screen);
 // ...
 }
 
 } LaunchScreenActivity.java ThemeUtils.ensureRuntimeTheme(this); Dynamically switch to the runtime theme

Slide 105

Slide 105 text

Don’t forget every screen is an entry point

Slide 106

Slide 106 text

Consider launch screen as boring screen

Slide 107

Slide 107 text

Don’t try to outsmart Android Embrace it!

Slide 108

Slide 108 text

That’s all Folks! @cyrilmottier • cyrilmottier.com