Slide 1

Slide 1 text

Building Overlay SDKs “The two-minute integra1on challenge”

Slide 2

Slide 2 text

A DIY survey tool

Slide 3

Slide 3 text

8k Android & iOS apps 230m mobile devices An SDK that overlays over everything runs on more than

Slide 4

Slide 4 text

Simple as A B C ● IntegraBon in 2 minutes

Slide 5

Slide 5 text

•  What is the purpose of this SDK? •  Who is it for? •  What tools/procedures are currently being used? •  How easy will it be to adopt and scale? •  How will you expose/distribute it? •  What are the plaBorm’s limitaDons? IniBal consideraBons when designing an SDK

Slide 6

Slide 6 text

An SDK to overlay quesBonnaires over any app •  Show overlay as full screen •  Usage as a black box for developers •  1 line of code •  Should work on Android 10 and above

Slide 7

Slide 7 text

An overlay is a view that is placed over other views. Such views can be created on Android in several different ways and for several different purposes, i.e.: •  FloaDng buNons/menus •  Onboarding tutorials •  AdverDsing •  Augmented Reality •  Fancy animaDons •  Other Overlays on Android

Slide 8

Slide 8 text

#1 Overlay through WindowManager Overlays on Android

Slide 9

Slide 9 text

A system service responsible for organizing the screen WindowManager

Slide 10

Slide 10 text

•  WindowToken is a special type of Binder object. •  Used on Android extensively for security reasons. •  A Window Token uniquely idenDfies a window, and is used by WindowManager to decide whether or not a window is allowed to be placed at a requested posiDon. Two types of WindowTokens: •  AppWindowToken •  WindowToken WindowTokens

Slide 11

Slide 11 text

There are 3 main classes of windows: •  ApplicaBon windows - are normal top level windows •  Sub-windows - are windows that are associated with another top-level window •  System windows - are special types of windows for use by the system for specific purposes Windows

Slide 12

Slide 12 text

Windows > adb shell dumpsys window windows > adb shell dumpsys window tokens

Slide 13

Slide 13 text

Adding an overlay view with WindowManager WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.width = WindowManager.LayoutParams.MATCH_PARENT; params.height = WindowManager.LayoutParams.MATCH_PARENT; params.type = WindowManager.LayoutParams.TYPE_APPLICATION; params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; params.format = PixelFormat.TRANSLUCENT; params.gravity = Gravity.CENTER | Gravity.START; windowManager.addView(overlayView, params);

Slide 14

Slide 14 text

Adding an overlay view with WindowManager

Slide 15

Slide 15 text

Adding an overlay view with WindowManager

Slide 16

Slide 16 text

#2 Overlay through PhoneWindow Overlays on Android

Slide 17

Slide 17 text

An implementaDon of Window’s abstract class When an acDvity starts, AcDvityManager along with WindowManager request the creaDon of a PhoneWindow. PhoneWindow is responsible for holding the acDvity’s view hierarchy. Phone Window

Slide 18

Slide 18 text

PhoneWindow has two important members: •  DecorView mDecor – which is the top-level view of the window, containing the window decor (like the acDvity's window background) •  ViewGroup mContentParent – which is the view in which the window contents are placed (for example the user's view layout) and is either the DecorView itself, or a child of DecorView where the contents go DecorView is an inner class of PhoneWindow: private final class DecorView extends FrameLayout implements RootViewSurfaceTaker { ... } Phone Window

Slide 19

Slide 19 text

Nexus 4 – Android 4.3 AcBvity’s view hierarchy prior seOng content view

Slide 20

Slide 20 text

Nexus 5 – Android 5.1 AcBvity’s view hierarchy prior seOng content view

Slide 21

Slide 21 text

There are 3 opDons on how to setContentView: 1.  setContentView (View view) - Set the acDvity content to an explicit view. 2.  setContentView (int layoutResID) - Set the acDvity content from a layout resource. 3.  setContentView (View view, ViewGroup.LayoutParams params) - Set the acDvity content to an explicit view with specified layout params. SeOng acBvity's content view @Override public void setContentView(View view, ViewGroup.LayoutParams params) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mContentParent.addView(view, params); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }

Slide 22

Slide 22 text

Nexus 5 – Android 5.1 AcBvity’s view hierarchy aXer seOng content view

Slide 23

Slide 23 text

setContentView Vs addContentView addContentView main difference with setContentView is that it does not remove any previously added views to mContentParent Adding another content view to the AcBvity addContentView(overlayView, new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); @Override public void addContentView(View view, ViewGroup.LayoutParams params) { if (mContentParent == null) { installDecor(); } mContentParent.addView(view, params); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } } ((ViewGroup) overlayView.getParent()).removeView(overlayView); removing is easy

Slide 24

Slide 24 text

Nexus 5 – Android 5.1 AcBvity’s view hierarchy aXer adding another content view

Slide 25

Slide 25 text

#3 Overlay with a new DecorView child Overlays on Android

Slide 26

Slide 26 text

•  FrameLayout - is designed to hold a stack of child views with the most recent placed on top. •  RelaBveLayout - can be used to posiDon child views either relaDve to each other, or to the screen boundaries. Overlay using a FrameLayout or RelaBveLayout

Slide 27

Slide 27 text

bringChildToFront(..) Changing z-order in a RelaBveLayout

Slide 28

Slide 28 text

Overlay with a new DecorView child – prior to re-arrangement Nexus 5 – Android 5.1

Slide 29

Slide 29 text

1.  Check if OverlayView already exists in view hierarchy. 2.  Remove OverlayView (if found) and restore to AcDvity's iniDal view hierarchy. 3.  Get reference to the child of DecorView which holds the AcDvity’s view hierarchy. 4.  Create parent RelaDveLayout. 5.  Remove child of DecorView's which holds the AcDvity’s view hierarchy from parent. 6.  Add the original DecorView child view to our parent RelaDveLayout. 7.  Add our parent RelaDveLayout to DecorView as a new child. 8.  Add our OverlayView to parent RelaDveLayout. Overlay with a new DecorView child – re-arrangement flow

Slide 30

Slide 30 text

Overlay with a new DecorView child – post re-arrangement Nexus 5 – Android 5.1

Slide 31

Slide 31 text

Choosing the right format Library project JAR apklib AAR

Slide 32

Slide 32 text

public staDc void init(AcDvity act, String apiKey, PosiDon p, int indPadding) ; public staDc void init(AcDvity act, String apiKey, PosiDon p, int indPadding, boolean devMode); public staDc void init(AcDvity act, String apiKey, PosiDon p, int indPadding, ViewGroup userLayout) ; public staDc void init( AcDvity act, String apiKey, PosiDon p, int indPadding, PollfishSurveyReceivedListener pollfishSurveyReceivedListener, PollfishSurveyNotAvailableListener pollfishSurveyNotAvailableListener, PollfishSurveyCompletedListener pollfishSurveyCompletedListener, PollfishOpenedListener pollfishOpenedListener, PollfishClosedListener pollfishClosedListener, PollfishUserNotEligibleListener pollfishUserNotEligibleListener) ; … public staDc void customInit (AcDvity act, String apiKey, PosiDon p, int indPadding) ; public staDc void customInit(AcDvity act, String apiKey, PosiDon p, int indPadding, boolean devMode) ; public staDc void customInit (AcDvity act, String apiKey, PosiDon p, int indPadding, ViewGroup userLayout) ; public staDc void customInit ( AcDvity act, String apiKey, PosiDon p, int indPadding, PollfishSurveyReceivedListener pollfishSurveyReceivedListener, PollfishSurveyNotAvailableListener pollfishSurveyNotAvailableListener, PollfishSurveyCompletedListener pollfishSurveyCompletedListener, PollfishOpenedListener pollfishOpenedListener, PollfishClosedListener pollfishClosedListener, PollfishUserNotEligibleListener pollfishUserNotEligibleListener) ; … Expose an interface for your library Think twice - choose the right design paAern!

Slide 33

Slide 33 text

ParamsBuilder paramsBuilder = new ParamsBuilder("YOUR_API_KEY") .indicatorPadding(50) .indicatorPosiDon(PosiDon.BOTTOM_RIGHT) .customMode(true) .pollfishSurveyCompletedListener(new PollfishSurveyCompletedListener() { @Override public void onPollfishSurveyCompleted(boolean playfulSurvey, int surveyPrice) { Log.d(TAG, "onPollfishSurveyCompleted"); } }) .build(); PollFish.initWith(this, paramsBuilder); Expose an interface for your library Easy to use, clean and extensible

Slide 34

Slide 34 text

In meta data of AndroidManifest.xml Provision and authenBcate - API key PollFish.initWith(.this, new ParamsBuilder("YOUR_API_KEY") .build()); or during iniDalizaDon

Slide 35

Slide 35 text

Give developers a way to test the behavior of your library transparently or give them back the power Developer Vs Release Mode boolean isDebuggable = (0 != (acDvity.getApplicaDonInfo().flags & ApplicaDonInfo.FLAG_DEBUGGABLE)); ParamsBuilder paramsBuilder = new ParamsBuilder("YOUR_API_KEY") .releaseMode(true) .build(); PollFish.initWith(this, paramsBuilder);

Slide 36

Slide 36 text

Throw an excepDon if needed/expected, fail fast Handle failure – Inform Inform developer – save the support team! Never fail in release mode if(paramsBuilder==null) { throw new IllegalArgumentExcepDon("ParamsBuilder should never be null"); } Log.w(TAG, "You re using Pollfish SDK for Google Play in developer mode"); log info to the developer

Slide 37

Slide 37 text

Request minimum permissions Check if a permission is granted during runDme Deal silently with permissions PackageManager pm = ctx.getPackageManager(); if ((pm.checkPermission(permission.BLUETOOTH, ctx.getPackageName()) == PackageManager.PERMISSION_GRANTED) && ((pm.checkPermission(permission.BLUETOOTH_ADMIN, ctx.getPackageName()) == PackageManager.PERMISSION_GRANTED))) { //Do some beacon staff }

Slide 38

Slide 38 text

protected boolean supportsBLE(Context ctx){ PackageManager pm = ctx.getPackageManager(); String feature_ble=PackageManager.FEATURE_BLUETOOTH_LE; return pm.hasSystemFeature(feature_ble); } Detect and use features under the hood

Slide 39

Slide 39 text

BluetoothAdapter mBluetoothAdapter=null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { BluetoothManager bluetoothManager = (BluetoothManager) acDvity.getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); } else { mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); } Keep backwards compaBbility – be agnosBc check Android version

Slide 40

Slide 40 text

Classpath DetecDon Keep backwards compaBbility – be agnosBc try { Class.forName("class.name" ); } catch( ClassNotFoundExcepDon e ) { //this class isn't there! } Thrown when the VM no1ces that a program tries to reference, * on a class or object, a method that does not exist. try{ setLayerType(View.LAYER_TYPE_SOFTWARE, null); }catch (NoSuchMethodError e) { //this method isn't there! }

Slide 41

Slide 41 text

You can allow AcDvity to implement listeners of SDK events Provide listeners/noBficaBons for SDK events ParamsBuilder paramsBuilder = new ParamsBuilder("YOUR_API_KEY"] .pollfishSurveyCompletedListener(new PollfishSurveyCompletedListener() { @Override public void onPollfishSurveyCompleted(boolean playfulSurvey, int surveyPrice) { Log.d(TAG, "onPollfishSurveyCompleted"); } }) .build(); public class MainAcDvity extends AppCompatAcDvity implements PollfishSurveyCompletedListener{…} if(acDvity instanceof PollfishSurveyCompletedListener){…} or allow developer to pass relevant listeners during iniDalizaDon and then internally check

Slide 42

Slide 42 text

ApplicaDon.AcDvityLifecycleCallbacks acDvityCallbackWrapper = new ApplicaDon.AcDvityLifecycleCallbacks() { public void onAcDvityCreated(AcDvity acDvity, Bundle bundle) { } public void onAcDvityStarted(AcDvity acDvity) { } public void onAcDvityResumed(AcDvity acDvity){ } public void onAcDvityPaused(AcDvity acDvity) { } public void onAcDvityStopped(AcDvity acDvity) { } public void onAcDvitySaveInstanceState(AcDvity acDvity, Bundle bundle) { } public void onAcDvityDestroyed(AcDvity acDvity) { } }; applicaDon.registerAcDvityLifecycleCallbacks(acDvityCallbackWrapper ); Listen/register to AcBvity lifecycle events if needed

Slide 43

Slide 43 text

@Override public boolean onKeyDown(int keyCode, KeyEvent event) { … If (keyCode == KeyEvent.KEYCODE_BACK) { if (pollfishViewStatus == PollfishViewStatus.PANEL_OPEN) { hidePanel(); return true; } } …. return false; } Capture and consume key events if needed

Slide 44

Slide 44 text

•  Handle offline •  Set threads priority •  Batch network requests, minimize impact •  Cache files •  Handle orientaDons •  Check keyboard and window flags •  Avoid memory leaks Design for every case

Slide 45

Slide 45 text

Make it testable/mockable •  Avoid staDc methods •  Avoid final classes Obfuscate and opBmize •  Keep public API Release •  Distribute to public repos Make it testable, obfuscate, release

Slide 46

Slide 46 text

•  An SDK easy to integrate – easy to use •  Javadocs and documentaDon •  Sample projects •  Demo apps Deliverables

Slide 47

Slide 47 text

Burn in support hell!

Slide 48

Slide 48 text

Thank you [email protected] @vourkosa