SlideShare a Scribd company logo
1 of 266
Google IO & Android Q
changes
by
Tim Surkis
Hadas Peled Gil Goldzweig
26/06/2019
Wifi Password: NEXT@tlv4ever
Tim Surkis
www.TSurkis.com
Mobile Software Engineer
Leading team
> 4,000 members Largest Android Active Community
What Do We Do?
● Fundamentals
● Advanced
● Design
● Hackathon
● Partner Events
● Mentors Program
What Do We Do?
● Fundamentals
● Advanced
● Design
● Hackathon
● Partner Events
● Mentors Program
Hadas Peled
Android Developer
at
Gil Goldzweig
Mobile team lead @ 10bis
Github
Twitter
● Kotlin
● In-App Updates
● Android Q Changes
○ New navigational system
○ Scoped Storage
○ Location
○ Settings Panel
○ Dark Mode
○ Bubble API
○ Sharing
● Jetpack Security
● Benchmark API
● Jetpack Compose
● CameraX
The plan
What’s new?
Kotlin
FIRST
Recommended kotlin course can be found here
in-app
Updates
API 21+
https://developer.android.com/guide/app-bundle/in-app-updates
In-App Update old way
In-App Update immediate in-app updates
In-App Update implementation
dependencies {
implementation 'com.google.android.play:core:1.5.0'
}
In-App Update implementation
private lateinit var updateManager: AppUpdateManager
override fun onCreate(savedInstanceState: Bundle?) {
updateManager = AppUpdateManagerFactory.create(this)
}
In-App Update implementation
private lateinit var updateManager: AppUpdateManager
override fun onCreate(savedInstanceState: Bundle?) {
updateManager = AppUpdateManagerFactory.create(this)
}
In-App Update immediate updates
override fun onResume() {
updateManager
.appUpdateInfo
.addOnSuccessListener { updateInfo ->
}
}
In-App Update immediate updates
.addOnSuccessListener { updateInfo ->
val updateInProgress =
updateInfo.updateAvailability() ==
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
val updateAvailable =
updateInfo.updateAvailability() ==
UpdateAvailability.UPDATE_AVAILABLE &&
updateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
...
}
In-App Update immediate updates
.addOnSuccessListener { updateInfo ->
val updateInProgress =
updateInfo.updateAvailability() ==
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
val updateAvailable =
updateInfo.updateAvailability() ==
UpdateAvailability.UPDATE_AVAILABLE &&
updateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
...
}
In-App Update immediate updates
.addOnSuccessListener { updateInfo ->
val updateInProgress =
updateInfo.updateAvailability() ==
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
val updateAvailable =
updateInfo.updateAvailability() ==
UpdateAvailability.UPDATE_AVAILABLE &&
updateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
...
}
In-App Update immediate updates
.addOnSuccessListener { updateInfo ->
val updateInProgress = /*...*/
val updateAvailable = /*...*/
if (updateInProgress || updateAvailable) {
updateManager.startUpdateFlowForResult(
updateInfo,
AppUpdateType.IMMEDIATE,
this,
updateRequestCode)
}
}
In-App Update immediate updates
.addOnSuccessListener { updateInfo ->
val updateInProgress = /*...*/
val updateAvailable = /*...*/
if (updateInProgress || updateAvailable) {
updateManager.startUpdateFlowForResult(
updateInfo,
AppUpdateType.IMMEDIATE,
this,
updateRequestCode)
}
}
In-App Update immediate updates
private val updateRequestCode: Int = 830
override fun onActivityResult(
requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
updateRequestCode -> {
}
}
}
In-App Update immediate updates
updateRequestCode -> {
when (resultCode) {
Activity.RESULT_OK -> {
// Shouldn't occur since the app restarts upon finishing
}
Activity.RESULT_CANCELED -> {
// user canceled or rejected update
}
ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> {
// update failed
}
}
}
In-App Update immediate updates
updateRequestCode -> {
when (resultCode) {
Activity.RESULT_OK -> {
// Shouldn't occur since the app restarts upon finishing
}
Activity.RESULT_CANCELED -> {
// user canceled or rejected update
}
ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> {
// update failed
}
}
}
In-App Update immediate updates
updateRequestCode -> {
when (resultCode) {
Activity.RESULT_OK -> {
// Shouldn't occur since the app restarts upon finishing
}
Activity.RESULT_CANCELED -> {
// user canceled or rejected update
}
ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> {
// update failed
}
}
}
In-App Update flexible in-app updates
In-App Update flexible updates
private lateinit var updateManager: AppUpdateManager
private lateinit var installListener: InstallStateUpdatedListener
override fun onCreate(savedInstanceState: Bundle?) {
updateManager = AppUpdateManagerFactory.create(this)
installListener = InstallStateUpdatedListener { installState ->
}
updateManager.registerListener(installListener)
}
In-App Update flexible updates
private lateinit var updateManager: AppUpdateManager
private lateinit var installListener: InstallStateUpdatedListener
override fun onCreate(savedInstanceState: Bundle?) {
updateManager = AppUpdateManagerFactory.create(this)
installListener = InstallStateUpdatedListener { installState ->
}
updateManager.registerListener(installListener)
}
In-App Update flexible updates
private lateinit var updateManager: AppUpdateManager
private lateinit var installListener: InstallStateUpdatedListener
override fun onCreate(savedInstanceState: Bundle?) {
updateManager = AppUpdateManagerFactory.create(this)
installListener = InstallStateUpdatedListener { installState ->
}
updateManager.registerListener(installListener)
}
In-App Update flexible updates InstallStatus
options
● CANCELED
● DOWNLOADED
● DOWNLOADING
● FAILED
● INSTALLED
● INSTALLING
● PENDING
● REQUIRES_UI_INTENT
● UNKNOWN
In-App Update flexible updates
private lateinit var updateManager: AppUpdateManager
private lateinit var installListener: InstallStateUpdatedListener
override fun onCreate(savedInstanceState: Bundle?) {
updateManager = AppUpdateManagerFactory.create(this)
installListener = InstallStateUpdatedListener { installState ->
}
updateManager.registerListener(installListener)
}
In-App Update flexible updates
override fun onDestroy() {
updateManager.unregisterListener(installListener)
}
In-App Update flexible updates
private fun requestUpdate() {
updateManager
.appUpdateInfo
.addOnSuccessListener { updateInfo ->
}
}
In-App Update flexible updates
private fun requestUpdate() {
updateManager
.appUpdateInfo
.addOnSuccessListener { updateInfo ->
}
}
In-App Update flexible updates
.addOnSuccessListener { updateInfo ->
val updateAvailable = updateInfo.updateAvailability() ==
UpdateAvailability.UPDATE_AVAILABLE
val updateAllowed = updateInfo.isUpdateTypeAllowed(
AppUpdateType.FLEXIBLE)
if (updateAvailable && updateAllowed) {
updateManager.startUpdateFlowForResult(
updateInfo,
AppUpdateType.FLEXIBLE,
activity,
updateRequestCode)
}
}
In-App Update flexible updates
.addOnSuccessListener { updateInfo ->
val updateAvailable = updateInfo.updateAvailability() ==
UpdateAvailability.UPDATE_AVAILABLE
val updateAllowed = updateInfo.isUpdateTypeAllowed(
AppUpdateType.FLEXIBLE)
if (updateAvailable && updateAllowed) {
updateManager.startUpdateFlowForResult(
updateInfo,
AppUpdateType.FLEXIBLE,
activity,
updateRequestCode)
}
}
In-App Update flexible updates
.addOnSuccessListener { updateInfo ->
val updateAvailable = updateInfo.updateAvailability() ==
UpdateAvailability.UPDATE_AVAILABLE
val updateAllowed = updateInfo.isUpdateTypeAllowed(
AppUpdateType.FLEXIBLE)
if (updateAvailable && updateAllowed) {
updateManager.startUpdateFlowForResult(
updateInfo,
AppUpdateType.FLEXIBLE,
activity,
updateRequestCode)
}
}
In-App Update flexible updates
.addOnSuccessListener { updateInfo ->
val updateAvailable = updateInfo.updateAvailability() ==
UpdateAvailability.UPDATE_AVAILABLE
val updateAllowed = updateInfo.isUpdateTypeAllowed(
AppUpdateType.FLEXIBLE)
if (updateAvailable && updateAllowed) {
updateManager.startUpdateFlowForResult(
updateInfo,
AppUpdateType.FLEXIBLE,
activity,
updateRequestCode)
}
}
In-App Update flexible updates
.addOnSuccessListener { updateInfo ->
val updateAvailable = updateInfo.updateAvailability() ==
UpdateAvailability.UPDATE_AVAILABLE
val updateAllowed = updateInfo.isUpdateTypeAllowed(
AppUpdateType.FLEXIBLE)
if (updateAvailable && updateAllowed) {
updateManager.startUpdateFlowForResult(
updateInfo,
AppUpdateType.FLEXIBLE,
this,
updateRequestCode)
}
}
In-App Update flexible updates
protected override fun onResume() {
updateManager
.appUpdateInfo
.addOnSuccessListener { updateInfo ->
when (updateInfo.installStatus()) {
InstallStatus.DOWNLOADED ->{
/*do something*/
}
// do something else
}
}
}
In-App Update flexible updates
protected override fun onResume() {
updateManager
.appUpdateInfo
.addOnSuccessListener { updateInfo ->
when (updateInfo.installStatus()) {
InstallStatus.DOWNLOADED -> {
/*do something*/
}
// do something else
}
}
}
In-App Update flexible updates
protected override fun onResume() {
updateManager
.appUpdateInfo
.addOnSuccessListener { updateInfo ->
when (updateInfo.installStatus()) {
InstallStatus.DOWNLOADED -> {
/*do something*/
}
// do something else
}
}
}
Questions?
Navigation history
API 5.0 Lollipop
API 9.0 Pie
API 10.0 Q*******
new navigation system
Gestures
https://developer.android.com/preview/features/gesturalnav
Gestures recents
Gestures home
Gestures back
Gestures
MyAPP
Implications system bounds
Safe Zone
Gestures
MyAPP
getSystemGestureInsets()
getMandatorySystemGestureInsest()
Implications system bounds
??????????
??????????????
View
Gestures Implications override insests
MyAPP
var exclusionRects = listOf(rect1, rect2)
fun onLayout(...) {
setSystemGestureExclusionRects(exclusionRects)
}
fun onDraw(...) {
setSystemGestureExclusionRects(exclusionRects)
}
Rect
top
bottom
rightleft
View
Gestures Implications override insests
MyAPP
var exclusionRects = listOf(rect1, rect2, rect3)
fun onLayout(...) {
setSystemGestureExclusionRects(exclusionRects)
}
fun onDraw(...) {
setSystemGestureExclusionRects(exclusionRects)
}
Rect
top
bottom
rightleft
Gestures Recomendations status and navigation
bars
MyAPP
rootView.systemUiVisibility =
(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
<!-- values-29/themes.xml: -->
<style name="AppTheme" parent="...">
<item name="android:navigationBarColor">
@android:color/transparent</item>
<item name="android:statusBarColor">
@android:color/transparent</item>
</style>
Questions?
Scoped Storage
https://developer.android.com/preview/privacy/scoped-storage
Scoped Storage accessing files
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Api 29+
android Q and higher
API 28-
android Pie and earlier
Scoped Storage
accessing app files
readwrite
MyApp Accessing App Storage
Photos MediaStore.Images
Videos MediaStore.Video
Audio MediaStore.Audio
* No permission needed!
Other files No access
Scoped Storage
accessing other app files
read
Photos MediaStore.Images
Videos MediaStore.Video
Audio MediaStore.Audio
Other files Storage Access Framework
downloads
DCIM
*android.permission.READ_EXTERNAL_STORAGE required
Scoped Storage
Storage Access Framework
readwrite
documentation: https://developer.android.com/guide/topics/providers/document-provider
Storage Access Framework
98,313 milliseconds
Conventional Android File I/O
3,139 millisecondsSource: https://www.xda-
developers.com/android-q-storage-access-
framework-scoped-storage/
Scoped Storage supporting legacy storage
<manifest>
<application
android:requestLegacyExternalStorage="true">
...
</application>
</manifest>
* Android R will only work with scoped storage
Questions?
Locations
Pre - Q
In - Q
When does my app considered to be in the
foreground?!
Foreground
Activity
Foreground
Service
Pre - Q
<manifest>
<!-- ACCESS_COARSE_LOCATION - city level -->
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- ACCESS_FINE_LOCATION - as best we can do -->
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"/>
...
</manifest>
Targets Q
<manifest>
<!-- ACCESS_COARSE_LOCATION - city level -->
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- ACCESS_FINE_LOCATION - as best we can do -->
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission
android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
...
</manifest>
Runs on Q -
BUT targets 9 or lower
Q
Let’s do it
//Target Q only, request "while-in-use" first
ActivityCompat.requestPermissions(this, arrayOf(
android.Manifest.permission.ACCESS_COARSE_LOCATION // or FINE
), REQUEST_CODE)
//Target Q only, request "while-in-use" first
ActivityCompat.requestPermissions(this, arrayOf(
android.Manifest.permission.ACCESS_COARSE_LOCATION // or FINE
), REQUEST_CODE)
//Request "all-the-time" (only if needed)
ActivityCompat.requestPermissions(this, arrayOf(
android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
), REQUEST_CODE)
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
//Location accessible in foreground
}
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
//Location accessible in foreground
}
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_BACKGROUND_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
//Location accessible in background
}
App permissions best practices
App permissions on OS upgrades
Device upgrade scenarios
Foreground service
Foreground service requires location type
<service
android:name="MyNavigationService"
android:foregroundServiceType="location" ... >
...
</service>
If you didn’t declare type location
● Has permission to location ‘while-in-use’ -
The service has NO permission for location
<service
android:name="MyNavigationService" ... >
</service>
If you didn’t declare type location
● Has permission to location ‘while-in-use’ -
The service has NO permission for location
● Has permission to location ‘all-the-time’ -
The service has permission for location
<service
android:name="MyNavigationService" ... >
</service>
All-the-time permission
+ fine location in background
Examples
● while-in-use: LocationUpdatesForegroundService on GitHub
● all-the-time: LocationUpdatesPendingIntent on GitHub
Questions?
Pre - Q
In - Q
Settings panel actions
val panelIntent = Intent(Settings.Panel.settings_panel_type)
startActivityForResult(panelIntent)
Settings panel actions
val panelIntent = Intent(Settings.Panel.settings_panel_type)
startActivityForResult(panelIntent)
● ACTION_INTERNET_CONNECTIVITY - Airplane mode, Wifi, Mobile Data.
Settings panel actions
val panelIntent = Intent(Settings.Panel.settings_panel_type)
startActivityForResult(panelIntent)
● ACTION_INTERNET_CONNECTIVITY - Airplane mode, Wifi, Mobile Data.
● ACTION_WIFI - Wifi only.
Settings panel actions
val panelIntent = Intent(Settings.Panel.settings_panel_type)
startActivityForResult(panelIntent)
● ACTION_INTERNET_CONNECTIVITY - Airplane mode, Wifi, Mobile Data.
● ACTION_WIFI - Wifi only.
● ACTION_NFC - All settings related to near-field communication (NFC).
Settings panel actions
val panelIntent = Intent(Settings.Panel.settings_panel_type)
startActivityForResult(panelIntent)
● ACTION_INTERNET_CONNECTIVITY - Airplane mode, Wifi, Mobile Data.
● ACTION_WIFI - Wifi only.
● ACTION_NFC - All settings related to near-field communication (NFC).
● ACTION_VOLUME - Volume settings for all audio streams.
Settings panel actions
Questions?
Why?
Why?
Environment
Why?
Environment Battery
Up to 60%
reduction in
power usage
Why?
Environment Battery Accessibility
How to enable
dark theme:
● Settings -> Display ->
Theme
How to enable
dark theme:
● Settings -> Display ->
Theme
● Quick Settings
How to enable
dark theme:
● Settings -> Display ->
Theme
● Quick Settings
● Battery Saver (Pixel devices)
Dark theme: app support
Strongly recommended that
apps will support dark theme.
DayNight
DayNight
● Enables the -night resource qualifier
when in dark theme
● Works back to Android 4.0 (API 14)
● Improved in AppCompat v1.1.0
<style name="MyTheme"
parent="Theme.AppCompat.DayNight">
</style>
<!-- OR -->
<style name="MyTheme"
parent="Theme.MaterialComponents.DayNight">
</style>
values/themes.xml
values-night/themes.xml
values/themes.xml
<style name="Theme.AppCompat.Daynight"
parent="Theme.AppCompat.Light">
values-night/themes.xml
values/themes.xml
<style name="Theme.AppCompat.Daynight"
parent="Theme.AppCompat.Light">
values-night/themes.xml
<style name="Theme.AppCompat.DayNight"
parent="Theme.AppCompat">
ModesDayNight
MODE_NIGHT_YES
//Always use the dark theme
ModesDayNight
MODE_NIGHT_YES
//Always use the dark theme
MODE_NIGHT_NO
//Always use the light theme DEFAULT PRE-Q
ModesDayNight
MODE_NIGHT_YES
//Always use the dark theme
MODE_NIGHT_NO
//Always use the light theme DEFAULT PRE-Q
MODE_NIGHT_FOLLOW_SYSTEM
//Follows the current system settings DEFAULT ON Q
ModesDayNight
MODE_NIGHT_YES
//Always use the dark theme
MODE_NIGHT_NO
//Always use the light theme DEFAULT PRE-Q
MODE_NIGHT_FOLLOW_SYSTEM
//Follows the current system settings DEFAULT ON Q
MODE_NIGHT_AUTO_BATTERY
//Dark when battery saver is enabled API 21+ AND AppCompat-v1.1.0
Support Night ModeDayNight
● AppCompatDelegate.setDefaultNightMode(mode : Int)
Support Night ModeDayNight
● AppCompatDelegate.setDefaultNightMode(mode : Int)
● getDelegate().setLocalNightMode(mode : Int)
Support Night ModeDayNight
● AppCompatDelegate.setDefaultNightMode(mode : Int)
● getDelegate().setLocalNightMode(mode : Int)
Note: the values are not persisted
Force/Smart Dark
Automatically convert app
to dark theme
implementing dark theme
<style name="Theme.MyApp"
parent="@style/Theme.MaterialComponents.Light">
<item name="android:forceDarkAllowed">true</item>
</style>
Seeing strange things?
Seeing strange things?
● xml - android:forceDarkAllowed="false"
● code - setForceDarkAllowed(false)
Force Dark Custom
google sample DayNight
Questions?
SAWs are Dangerous
SAWs* are Dangerous
*SYSTEM_ALERT_WINDOW
SAWs* are Dangerous
*SYSTEM_ALERT_WINDOW
● Lots of apps are utilizing SAW
to do cool things.
SAWs* are Dangerous
*SYSTEM_ALERT_WINDOW
● Lots of apps are utilizing SAW
to do cool things.
● Unfortunately some apps are
utilizing SAW for some
less cool things.
Bubbles
● They float on top of other app
content and follow the user
Bubbles
● They float on top of other app
content and follow the user
● Can be expanded to reveal app
functionality and information
Bubbles
● They float on top of other app
content and follow the user
● Can be expanded to reveal app
functionality and information
● Built into the notification system
Build a Bubble
● Configure the activity shown in the bubble
● Construct BubbleMetadata
● Add the metadata to your notification and send
Configure the activity shown in the bubble
<!--Must be embeddable,documentLaunchMode always,and resizable-->
<activity
android:name=".bubbles.BubbleActivity"
...
/>
Configure the activity shown in the bubble
<!--Must be embeddable,documentLaunchMode always,and resizable-->
<activity
android:name=".bubbles.BubbleActivity"
android:allowEmbedded="true"
...
/>
Configure the activity shown in the bubble
<!--Must be embeddable,documentLaunchMode always,and resizable-->
<activity
android:name=".bubbles.BubbleActivity"
android:allowEmbedded="true"
android:documentLaunchMode="always"
...
/>
Configure the activity shown in the bubble
<!--Must be embeddable,documentLaunchMode always,and resizable-->
<activity
android:name=".bubbles.BubbleActivity"
android:allowEmbedded="true"
android:documentLaunchMode="always"
android:resizeableActivity="true"
...
/>
Construct BubbleMetadata
val intent = Intent(mContext, MainActivity::class.java)
val bubbleIntent = PendingIntent.getActivity(
mContext, 0 /* requestCode */, intent, 0/* flags*/)
Construct BubbleMetadata
val intent = Intent(mContext, MainActivity::class.java)
val bubbleIntent = PendingIntent.getActivity(
mContext, 0 /* requestCode */, intent, 0/* flags*/)
//Metadata to go on the notification
val metadata = Notification.BubbleMetadata.Builder()
Construct BubbleMetadata
val intent = Intent(mContext, MainActivity::class.java)
val bubbleIntent = PendingIntent.getActivity(
mContext, 0 /* requestCode */, intent, 0/* flags*/)
//Metadata to go on the notification
val metadata = Notification.BubbleMetadata.Builder()
//Icon is not displayed in Beta Q
.setIcon(Icon.createWithResource(mContext,
R.drawable.ic_bubble))
Construct BubbleMetadata
val intent = Intent(mContext, MainActivity::class.java)
val bubbleIntent = PendingIntent.getActivity(
mContext, 0 /* requestCode */, intent, 0/* flags*/)
//Metadata to go on the notification
val metadata = Notification.BubbleMetadata.Builder()
//Icon is not displayed in Beta Q
.setIcon(Icon.createWithResource(mContext,
R.drawable.ic_bubble))
.setIntent(bubbleIntent).build()
Add the BubbleMetadata to your
notification and send
var builder = Notification.Builder(mContext, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
...
.setBubbleMetadata(metadata)
Add the BubbleMetadata to your
notification and send
var builder = Notification.Builder(mContext, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
...
.setBubbleMetadata(metadata)
//Send the notification
mNotificationManager.notify(BUBBLE_ID, builder.build())
When to use Bubbles?
● Messaging
● Calling
● User initiated
Questions?
Queijadas
Sharing
● Content preview - supports
images and text
Sharing
● Content preview - supports
images and text
● New sharing shortcut API -
Doesn’t need to start your app
Sharing
● Content preview - supports
images and text
● New sharing shortcut API -
Doesn’t need to start your app
● Copy to clipboard up top
Sharing
● Content preview - supports
images and text
● New sharing shortcut API -
Doesn’t need to start your app
● Copy to clipboard up top
● It’s really really fast
Providing a
sharing shortcut
Providing a sharing shortcut
val sic = mutableListOf<ShortcutInfoCompat>()
Providing a sharing shortcut
val sic = mutableListOf<ShortcutInfoCompat>()
sic.add( ShortcutInfoCompat.Builder(context, personId)
Providing a sharing shortcut
val sic = mutableListOf<ShortcutInfoCompat>()
sic.add( ShortcutInfoCompat.Builder(context, personId)
.setShortLabel("Chet")
Providing a sharing shortcut
val sic = mutableListOf<ShortcutInfoCompat>()
sic.add( ShortcutInfoCompat.Builder(context, personId)
.setShortLabel("Chet")
.setLongLabel("Chet Haase")
Providing a sharing shortcut
val sic = mutableListOf<ShortcutInfoCompat>()
sic.add( ShortcutInfoCompat.Builder(context, personId)
.setShortLabel("Chet")
.setLongLabel("Chet Haase")
.setPerson(...)//Person can improve ranking
Providing a sharing shortcut
val sic = mutableListOf<ShortcutInfoCompat>()
sic.add( ShortcutInfoCompat.Builder(context, personId)
.setShortLabel("Chet")
.setLongLabel("Chet Haase")
.setPerson(...)//Person can improve ranking
.setIcon(/* Icon */)
Providing a sharing shortcut
val sic = mutableListOf<ShortcutInfoCompat>()
sic.add( ShortcutInfoCompat.Builder(context, personId)
.setShortLabel("Chet")
.setLongLabel("Chet Haase")
.setPerson(...)//Person can improve ranking
.setIcon(/* Icon */)
.setCategories(category)//required to be a Sharing Shortcut
Providing a sharing shortcut
val sic = mutableListOf<ShortcutInfoCompat>()
sic.add( ShortcutInfoCompat.Builder(context, personId)
.setShortLabel("Chet")
.setLongLabel("Chet Haase")
.setPerson(...)//Person can improve ranking
.setIcon(/* Icon */)
.setCategories(category)//required to be a Sharing Shortcut
.setIntent(...).build())
Providing a sharing shortcut
val sic = mutableListOf<ShortcutInfoCompat>()
sic.add( ShortcutInfoCompat.Builder(context, personId)
.setShortLabel("Chet")
.setLongLabel("Chet Haase")
.setPerson(...)//Person can improve ranking
.setIcon(/* Icon */)
.setCategories(category)//required to be a Sharing Shortcut
.setIntent(...).build())
ShortcutManagerCompat.addDynamicShortcuts(context, sic)
Adding a rich
preview content
Adding a rich preview content
var share = Intent(ACTION_SEND)
...
Adding a rich preview content
var share = Intent(ACTION_SEND)
...
//Metadata must be in intent before Intent#crateChooser
share.putExtra(EXTRA_TITLE,"Introducing Android Q Beta")
Adding a rich preview content
var share = Intent(ACTION_SEND)
...
//Metadata must be in intent before Intent#crateChooser
share.putExtra(EXTRA_TITLE,"Introducing Android Q Beta")
share.clipData = ClipData.newUri(contentResolver,
"thumbnail", myContentProviderUri)
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
Adding a rich preview content
var share = Intent(ACTION_SEND)
...
//Metadata must be in intent before Intent#crateChooser
share.putExtra(EXTRA_TITLE,"Introducing Android Q Beta")
share.clipData = ClipData.newUri(contentResolver,
"thumbnail", myContentProviderUri)
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
share = Intent.createChooser(share,null /* no longer used */)
Questions ?
Queen of Puddings
Alpha
Jetpack security
Jetpack security - key
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
Jetpack security - File
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val encryptedFile = EncryptedFile.Builder(
).build()
Jetpack security - File
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val encryptedFile = EncryptedFile.Builder(
File(context.getFilesDir(), "/your/file"),
).build()
Jetpack security - File
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val encryptedFile = EncryptedFile.Builder(
File(context.getFilesDir(), "/your/file"),
context,
).build()
Jetpack security - File
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val encryptedFile = EncryptedFile.Builder(
File(context.getFilesDir(), "/your/file"),
context,
masterKey,
).build()
Jetpack security - File
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val encryptedFile = EncryptedFile.Builder(
File(context.getFilesDir(), "/your/file"),
context,
masterKey,
EncryptedFileKeyset.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
Jetpack security - Read File
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val encryptedFile = EncryptedFile.Builder(
File(context.getFilesDir(), "/your/file"),
context,
masterKey,
EncryptedFileKeyset.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
encryptedFile.openInputStream()
Jetpack security - Write File
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val encryptedFile = EncryptedFile.Builder(
File(context.getFilesDir(), "/your/file"),
context,
masterKey,
EncryptedFileKeyset.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
encryptedFile.openInputStrem()
encryptedFile.openOutputStream()
Jetpack security - SharedPreferences
Jetpack security - SharedPreferences
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
Jetpack security - SharedPreferences
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val sharedPreferences = EncryptedSharedPreferences.create(
)
Jetpack security - SharedPreferences
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val sharedPreferences = EncryptedSharedPreferences.create(
"shareadpreferencesName",
)
Jetpack security - SharedPreferences
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val sharedPreferences = EncryptedSharedPreferences.create(
"shareadpreferencesName",
masterKey,
)
Jetpack security - SharedPreferences
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val sharedPreferences = EncryptedSharedPreferences.create(
"shareadpreferencesName",
masterKey,
context,
)
Jetpack security - SharedPreferences
val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
val sharedPreferences = EncryptedSharedPreferences.create(
"shareadpreferencesName",
masterKey,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
Jetpack security
● Currently in alpha
● Min api 23
● More information available here
Questions?
Benchmark
● Makes it easy to benchmark your code
● Handles warmup and clock stability for you
● Gives you a accurate result
● Runs as instrumented test
What should I benchmark
● RecyclerView scrolling
● Data processing
● Pieces of code that get used repeatedly
Benchmark
Benchmark
@RunWith(AndroidJUnit4::class)
class MyBenchmark() {
}
Benchmark
@RunWith(AndroidJUnit4::class)
class MyBenchmark() {
@get:Rule
val rule = BenchmarkRule()
}
Benchmark
@RunWith(AndroidJUnit4::class)
class MyBenchmark() {
@get:Rule
val rule = BenchmarkRule()
@Test
fun simpleViewInflate() {
}
}
Benchmark
@RunWith(AndroidJUnit4::class)
class MyBenchmark() {
@get:Rule
val rule = BenchmarkRule()
@Test
fun simpleViewInflate() {
val context = ApplicationProvider.getApplicationContext()
val inflater = LayoutInflater.from(context)
val root = FrameLayout(context)
}
}
Benchmark
@RunWith(AndroidJUnit4::class)
class MyBenchmark() {
@get:Rule
val rule = BenchmarkRule()
@Test
fun simpleViewInflate() {
val context = ApplicationProvider.getApplicationContext()
val inflater = LayoutInflater.from(context)
val root = FrameLayout(context)
rule.measureRepeated {
inflater.inflate(R.layout.test_simple_view, root, false)
}
}
}
Benchmark
Benchmark
Benchmark
/storage/emulated/0/Download/app_id-benchmarkData.json
Benchmark
● Available as part of AndroidX
● Currently in alpha
● You can learn more about Benchmark here
Questions?
Spinner?
🤔
==
Spinner
Jetpack compose
Jetpack compose
UI is defined as a function
● Take data as input
● Emits UI hierarchy when invoked
@Composable
fun Greeting(name: String) {
Text("Hello: $name")
}
@Composable
fun Greeting(name: String) {
Text("Hello: $name")
}
Hello: Gil
What is compose?
● A set of jetpack UI widgets
● A Kotlin compiler plugin
● Unbundled from platform release
● Uses canvas instead of Views
● Fully compatible with your existing app/code
Jetpack compose
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState, persistentState)
setContent {
Greeting("Android academy")
}
}
}
Jetpack compose
@Composable
fun Greeting(name: String) {
Text("Hello: $name")
}
Jetpack compose - List
@Composable
fun Greeting(vararg names: String) {
Text("Hello: $name")
}
@Composable
fun Greeting(vararg names: String) {
List {
Text("Hello: $name")
}
}
Jetpack compose - List
@Composable
fun Greeting(vararg names: String) {
List {
for (name in names) {
Text("Hello: $name")
}
}
}
Jetpack compose - List
One way data flow
Data
Events
Spinner
Item Clicked
Value changed
OnItemSelected
Item Clicked
OnItemSelected
UI changes Value changed
UI changes
Jetpack compose
Jetpack compose
fun Counter() {
}
Jetpack compose
@Composable
fun Counter() {
}
Jetpack compose
@Composable
fun Counter() {
val amount
}
Jetpack compose
@Composable
fun Counter() {
val amount = state { 0 }
}
Jetpack compose
@Composable
fun Counter() {
val amount = +state { 0 }
}
Jetpack compose
@Composable
fun Counter() {
val amount = +state { 0 }
Column {
}
}
Jetpack compose
@Composable
fun Counter() {
val amount = +state { 0 }
Column {
Text("Counter demo")
}
}
Jetpack compose
@Composable
fun Counter() {
val amount = +state { 0 }
Column {
Text("Counter demo")
Button(text = "Add",
onClick = { amount.value++ })
Button(text = "Subtract",
onClick = { amount.value-- })
}
}
Jetpack compose
@Composable
fun Counter() {
val amount = +state { 0 }
Column {
Text("Counter demo")
Button(text = "Add",
onClick = { amount.value++ })
Button(text = "Subtract",
onClick = { amount.value-- })
Text("Clicks: ${amount.value}")
}
}
Jetpack compose
● Currently in pre alpha (Very early stage)
● Available under AOSP and can be used here
● Join the slack channel
Questions?
Declarative UI Patterns
(Google I/O'19)
Camera is hard...
Easy….
private val captureCallback = object : CameraCaptureSession.CaptureCallback() {
private fun process(result: CaptureResult) {
when (state) {
STATE_PREVIEW -> Unit // Do nothing when the camera preview is working normally.
STATE_WAITING_LOCK -> capturePicture(result)
STATE_WAITING_PRECAPTURE -> {
// CONTROL_AE_STATE can be null on some devices
val aeState = result.get(CaptureResult.CONTROL_AE_STATE)
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
state = STATE_WAITING_NON_PRECAPTURE
}
}
STATE_WAITING_NON_PRECAPTURE -> {
// CONTROL_AE_STATE can be null on some devices
val aeState = result.get(CaptureResult.CONTROL_AE_STATE)
if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
state = STATE_PICTURE_TAKEN
captureStillPicture()
}
}
}
}
private fun capturePicture(result: CaptureResult) {
val afState = result.get(CaptureResult.CONTROL_AF_STATE)
if (afState == null) {
captureStillPicture()
} else if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED
|| afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
// CONTROL_AE_STATE can be null on some devices
val aeState = result.get(CaptureResult.CONTROL_AE_STATE)
if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
state = STATE_PICTURE_TAKEN
captureStillPicture()
} else {
runPrecaptureSequence()
}
}
}
override fun onCaptureProgressed(session: CameraCaptureSession,
request: CaptureRequest,
partialResult: CaptureResult) {
process(partialResult)
}
override fun onCaptureCompleted(session: CameraCaptureSession,
request: CaptureRequest,
result: TotalCaptureResult) {
process(result)
}
}
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_camera2_basic, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<View>(R.id.picture).setOnClickListener(this)
view.findViewById<View>(R.id.info).setOnClickListener(this)
textureView = view.findViewById(R.id.texture)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
file = File(activity.getExternalFilesDir(null), PIC_FILE_NAME)
}
nsor coordinate
* @param maxWidth The maximum width that can be chosen
* @param maxHeight The maximum height that can be chosen
* @param aspectRatio The aspect ratio
* @return The optimal `Size`, or an arbitrary one if none were big enough
*/
@JvmStatic private fun chooseOptimalSize(
choices: Array<Size>,
textureViewWidth: Int,
textureViewHeight: Int,
maxWidth: Int,
maxHeight: Int,
aspectRatio: Size
): Size {
// Collect the supported resolutions that are at least as big as the preview Surface
val bigEnough = ArrayList<Size>()
// Collect the supported resolutions that are smaller than the preview Surface
val notBigEnough = ArrayList<Size>()
val w = aspectRatio.width
val h = aspectRatio.height
for (option in choices) {
if (option.width <= maxWidth && option.height <= maxHeight &&
option.height == option.width * h / w) {
if (option.width >= textureViewWidth && option.height >= textureViewHeight) {
bigEnough.add(option)
} else {
notBigEnough.add(option)
}
}
}
// Pick the smallest of those big enough. If there is no one big enough, pick the
// largest of those not big enough.
if (bigEnough.size > 0) {
return Collections.min(bigEnough, CompareSizesByArea())
} else if (notBigEnough.size > 0) {
return Collections.max(notBigEnough, CompareSizesByArea())
} else {
Log.e(TAG, "Couldn't find any suitable preview size")
return choices[0]
}
}
@JvmStatic fun newInstance(): Camera2BasicFragment = Camera2BasicFragment()
}
}
override fun onResume() {
super.onResume()
startBackgroundThread()
// When the screen is turned off and turned back on, the SurfaceTexture is already
// available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
// a camera and start preview from here (otherwise, we wait until the surface is ready in
// the SurfaceTextureListener).
if (textureView.isAvailable) {
openCamera(textureView.width, textureView.height)
} else {
textureView.surfaceTextureListener = surfaceTextureListener
}
}
override fun onPause() {
closeCamera()
stopBackgroundThread()
super.onPause()
}
private fun requestCameraPermission() {
if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
ConfirmationDialog().show(childFragmentManager, FRAGMENT_DIALOG)
} else {
requestPermissions(arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
}
}
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<String>,
grantResults: IntArray) {
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.size != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
ErrorDialog.newInstance(getString(R.string.request_permission))
.show(childFragmentManager, FRAGMENT_DIALOG)
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
/**
* Sets up member variables related to camera.
*
* @param width The width of available size for camera preview
* @param height The height of available size for camera preview
*/
private fun setUpCameraOutputs(width: Int, height: Int) {
val manager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
for (cameraId in manager.cameraIdList) {
val characteristics = manager.getCameraCharacteristics(cameraId)
// We don't use a front facing camera in this sample.
val cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING)
if (cameraDirection != null &&
cameraDirection == CameraCharacteristics.LENS_FACING_FRONT) {
continue
}
val map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue
// For still image captures, we use the largest available size.
val largest = Collections.max(
Arrays.asList(*map.getOutputSizes(ImageFormat.JPEG)),
CompareSizesByArea())
imageReader = ImageReader.newInstance(largest.width, largest.height,
ImageFormat.JPEG, /*maxImages*/ 2).apply {
setOnImageAvailableListener(onImageAvailableListener, backgroundHandler)
}
// Find out if we need to swap dimension to get the preview size relative to sensor
// coordinate.
val displayRotation = activity.windowManager.defaultDisplay.rotation
sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)
val swappedDimensions = areDimensionsSwapped(displayRotation)
val displaySize = Point()
activity.windowManager.defaultDisplay.getSize(displaySize)
val rotatedPreviewWidth = if (swappedDimensions) height else width
val rotatedPreviewHeight = if (swappedDimensions) width else height
var maxPreviewWidth = if (swappedDimensions) displaySize.y else displaySize.x
var maxPreviewHeight = if (swappedDimensions) displaySize.x else displaySize.y
if (maxPreviewWidth > MAX_PREVIEW_WIDTH) maxPreviewWidth = MAX_PREVIEW_WIDTH
if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) maxPreviewHeight = MAX_PREVIEW_HEIGHT
// Danger, W.R.! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture::class.java),
rotatedPreviewWidth, rotatedPreviewHeight,
maxPreviewWidth, maxPreviewHeight,
largest)
// We fit the aspect ratio of TextureView to the size of preview we picked.
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
textureView.setAspectRatio(previewSize.width, previewSize.height)
} else {
textureView.setAspectRatio(previewSize.height, previewSize.width)
}
// Check if the flash is supported.
flashSupported =
characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true
val captureBuilder = cameraDevice?.createCaptureRequest(
CameraDevice.TEMPLATE_STILL_CAPTURE)?.apply {
addTarget(imageReader?.surface)
// Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X)
// We have to take that into account and rotate JPEG properly.
// For devices with orientation of 90, we return our mapping from ORIENTATIONS.
// For devices with orientation of 270, we need to rotate the JPEG 180 degrees.
set(CaptureRequest.JPEG_ORIENTATION,
(ORIENTATIONS.get(rotation) + sensorOrientation + 270) % 360)
// Use the same AE and AF modes as the preview.
set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
}?.also { setAutoFlash(it) }
val captureCallback = object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(session: CameraCaptureSession,
request: CaptureRequest,
result: TotalCaptureResult) {
activity.showToast("Saved: $file")
Log.d(TAG, file.toString())
unlockFocus()
}
}
captureSession?.apply {
stopRepeating()
abortCaptures()
capture(captureBuilder?.build(), captureCallback, null)
}
} catch (e: CameraAccessException) {
Log.e(TAG, e.toString())
}
}
/**
* Unlock the focus. This method should be called when still image capture sequence is
* finished.
*/
private fun unlockFocus() {
try {
// Reset the auto-focus trigger
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_CANCEL)
setAutoFlash(previewRequestBuilder)
captureSession?.capture(previewRequestBuilder.build(), captureCallback,
backgroundHandler)
// After this, the camera will go back to the normal state of preview.
state = STATE_PREVIEW
captureSession?.setRepeatingRequest(previewRequest, captureCallback,
backgroundHandler)
} catch (e: CameraAccessException) {
Log.e(TAG, e.toString())
}
}
override fun onClick(view: View) {
when (view.id) {
R.id.picture -> lockFocus()
R.id.info -> {
if (activity != null) {
AlertDialog.Builder(activity)
.setMessage(R.string.intro_message)
.setPositiveButton(android.R.string.ok, null)
.show()
}
}
}
}
private fun setAutoFlash(requestBuilder: CaptureRequest.Builder) {
if (flashSupported) {
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)
}
}
companion object {
/**
* Conversion from screen rotation to JPEG orientation.
*/
private val ORIENTATIONS = SparseIntArray()
private val FRAGMENT_DIALOG = "dialog"
init {
ORIENTATIONS.append(Surface.ROTATION_0, 90)
ORIENTATIONS.append(Surface.ROTATION_90, 0)
ORIENTATIONS.append(Surface.ROTATION_180, 270)
ORIENTATIONS.append(Surface.ROTATION_270, 180)
}
/**
* Tag for the [Log].
*/
private val TAG = "Camera2BasicFragment"
/**
* Camera state: Showing camera preview.
*/
private val STATE_PREVIEW = 0
/**
* Camera state: Waiting for the focus to be locked.
*/
private val STATE_WAITING_LOCK = 1
/**
* Camera state: Waiting for the exposure to be precapture state.
*/
private val STATE_WAITING_PRECAPTURE = 2
/**
* Camera state: Waiting for the exposure state to be something other than precapture.
*/
private val STATE_WAITING_NON_PRECAPTURE = 3
/**
* Camera state: Picture was taken.
*/
private val STATE_PICTURE_TAKEN = 4
/**
* Max preview width that is guaranteed by Camera2 API
*/
private val MAX_PREVIEW_WIDTH = 1920
/**
* Max preview height that is guaranteed by Camera2 API
*/
private val MAX_PREVIEW_HEIGHT = 1080
/**
this.cameraId = cameraId
// We've found a viable camera and finished setting up member variables,
// so we don't need to iterate through other available cameras.
return
}
} catch (e: CameraAccessException) {
Log.e(TAG, e.toString())
} catch (e: NullPointerException) {
// Currently an NPE is thrown when the Camera2API is used but not supported on the
// device this code runs.
ErrorDialog.newInstance(getString(R.string.camera_error))
.show(childFragmentManager, FRAGMENT_DIALOG)
}
}
/**
* Determines if the dimensions are swapped given the phone's current rotation.
*
* @param displayRotation The current rotation of the display
*
* @return true if the dimensions are swapped, false otherwise.
*/
private fun areDimensionsSwapped(displayRotation: Int): Boolean {
var swappedDimensions = false
when (displayRotation) {
Surface.ROTATION_0, Surface.ROTATION_180 -> {
if (sensorOrientation == 90 || sensorOrientation == 270) {
swappedDimensions = true
}
}
Surface.ROTATION_90, Surface.ROTATION_270 -> {
if (sensorOrientation == 0 || sensorOrientation == 180) {
swappedDimensions = true
}
}
else -> {
Log.e(TAG, "Display rotation is invalid: $displayRotation")
}
}
return swappedDimensions
}
/**
* Opens the camera specified by [Camera2BasicFragment.cameraId].
*/
private fun openCamera(width: Int, height: Int) {
val permission = ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)
if (permission != PackageManager.PERMISSION_GRANTED) {
requestCameraPermission()
return
}
setUpCameraOutputs(width, height)
configureTransform(width, height)
val manager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
// Wait for camera to open - 2.5 seconds is sufficient
if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw RuntimeException("Time out waiting to lock camera opening.")
}
manager.openCamera(cameraId, stateCallback, backgroundHandler)
} catch (e: CameraAccessException) {
Log.e(TAG, e.toString())
} catch (e: InterruptedException) {
throw RuntimeException("Interrupted while trying to lock camera opening.", e)
}
}
/**
* Closes the current [CameraDevice].
*/
private fun closeCamera() {
try {
cameraOpenCloseLock.acquire()
captureSession?.close()
captureSession = null
cameraDevice?.close()
cameraDevice = null
imageReader?.close()
imageReader = null
} catch (e: InterruptedException) {
throw RuntimeException("Interrupted while trying to lock camera closing.", e)
} finally {
cameraOpenCloseLock.release()
}
}
/**
* Starts a background thread and its [Handler].
*/
private fun startBackgroundThread() {
backgroundThread = HandlerThread("CameraBackground").also { it.start() }
backgroundHandler = Handler(backgroundThread?.looper)
}
/**
* Stops the background thread and its [Handler].
hen we get a response in
* [.captureCallback] from both [.lockFocus].
*/
private fun captureStillPicture() {
try {
if (activity == null || cameraDevice == null) return
val rotation = activity.windowManager.defaultDisplay.rotation
// This is the CaptureRequest.Builder that we use to take a picture.
e
CameraX
Camera
X
● Simplified usage
● Solve many bugs and pain points
● Consistency across devices
Simplified usage
● Preview
● Image analysis
● Image capture
Preview
Preview
val previewConfig = PreviewConfig.Builder().build()
Preview
val previewConfig = PreviewConfig.Builder().build()
val preview = Preview(previewConfig)
Preview
val previewConfig = PreviewConfig.Builder().build()
val preview = Preview(previewConfig)
preview.setOnPreviewOutputUpdateListener {
previewOutput: Preview.PreviewOutput? ->
/* Your code here.
* For example, use previewOutput?.getSurfaceTexture()
* and post to a GL renderer.
*/
}
Preview
val previewConfig = PreviewConfig.Builder().build()
val preview = Preview(previewConfig)
preview.setOnPreviewOutputUpdateListener {
previewOutput: Preview.PreviewOutput? ->
/* Your code here.
* For example, use previewOutput?.getSurfaceTexture()
* and post to a GL renderer.
*/
}
CameraX.bindToLifecycle(this as LifecycleOwner, preview)
Image analysis
Image analysis
Image analysis
val imageAnalysisConfig = ImageAnalysisConfig.Builder()
Image analysis
val imageAnalysisConfig = ImageAnalysisConfig.Builder()
.setTargetResolution(Size(1280, 720))
Image analysis
val imageAnalysisConfig = ImageAnalysisConfig.Builder()
.setTargetResolution(Size(1280, 720))
.build()
Image analysis
val imageAnalysisConfig = ImageAnalysisConfig.Builder()
.setTargetResolution(Size(1280, 720))
.build()
val imageAnalysis = ImageAnalysis(imageAnalysisConfig)
Image analysis
val imageAnalysisConfig = ImageAnalysisConfig.Builder()
.setTargetResolution(Size(1280, 720))
.build()
val imageAnalysis = ImageAnalysis(imageAnalysisConfig)
imageAnalysis.setAnalyzer {
image: ImageProxy, rotationDegrees: Int ->
//insert your analysis code here
}
Image analysis
val imageAnalysisConfig = ImageAnalysisConfig.Builder()
.setTargetResolution(Size(1280, 720))
.build()
val imageAnalysis = ImageAnalysis(imageAnalysisConfig)
imageAnalysis.setAnalyzer {
image: ImageProxy, rotationDegrees: Int ->
//insert your analysis code here
}
CameraX.bindToLifecycle(this as LifecycleOwner, imageAnalysis, preview)
Image capture
Image capture
val imageCaptureConfig = ImageCaptureConfig.Builder()
Image capture
val imageCaptureConfig = ImageCaptureConfig.Builder()
.setTargetRotation(windowManager.defaultDisplay.rotation)
Image capture
val imageCaptureConfig = ImageCaptureConfig.Builder()
.setTargetRotation(windowManager.defaultDisplay.rotation)
.build()
Image capture
val imageCaptureConfig = ImageCaptureConfig.Builder()
.setTargetRotation(windowManager.defaultDisplay.rotation)
.build()
val imageCapture = ImageCapture(imageCaptureConfig)
Image capture
val imageCaptureConfig = ImageCaptureConfig.Builder()
.setTargetRotation(windowManager.defaultDisplay.rotation)
.build()
val imageCapture = ImageCapture(imageCaptureConfig)
CameraX.bindToLifecycle(this as LifecycleOwner,
imageCapture, imageAnalysis, preview)
Save an Image on user action
fun onClick() {
}
fun onClick() {
val file = File(...)
}
Save an Image on user action
fun onClick() {
val file = File(...)
imageCapture.takePicture(
)
}
Save an Image on user action
fun onClick() {
val file = File(...)
imageCapture.takePicture(file,
)
}
Save an Image on user action
fun onClick() {
val file = File(...)
imageCapture.takePicture(file,
object : ImageCapture.OnImageSavedListener {
override fun onError(error: ImageCapture.UseCaseError,
message: String, exc: Throwable?) {
// insert your code here.
}
override fun onImageSaved(file: File) {
// insert your code here.
}
})
}
Listen to save result
Extensions
Portrait NightBeautyHDR
Extensions
val builder = ImageCaptureConfig.Builder()
Extensions
val builder = ImageCaptureConfig.Builder()
val bokehImageCapture = BokehImageCaptureExtender.create(builder)
Extensions
val builder = ImageCaptureConfig.Builder()
val bokehImageCapture = BokehImageCaptureExtender.create(builder)
if (bokehImageCapture.isExtensionAvailable()) {
bokehImageCapture.enableExtension()
}
CameraX
● Currently in alpha
● Available under AndroidX
● You can read more here
Questions?
Thank you!!!!

More Related Content

What's hot

Manipulating Android tasks and back stack
Manipulating Android tasks and back stackManipulating Android tasks and back stack
Manipulating Android tasks and back stackRan Nachmany
 
Summit 2015: Mobile App Dev and Content Management with Adobe Experience Manager
Summit 2015: Mobile App Dev and Content Management with Adobe Experience ManagerSummit 2015: Mobile App Dev and Content Management with Adobe Experience Manager
Summit 2015: Mobile App Dev and Content Management with Adobe Experience Managerbrucelefebvre
 
Hassle-Free Continuous Integration with Real Device Testing
Hassle-Free Continuous Integration with Real Device TestingHassle-Free Continuous Integration with Real Device Testing
Hassle-Free Continuous Integration with Real Device TestingBitbar
 
Rohit android lab projects in suresh gyan vihar
Rohit android lab projects in suresh gyan viharRohit android lab projects in suresh gyan vihar
Rohit android lab projects in suresh gyan viharRohit malav
 
Let's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UILet's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UIAtlassian
 
Joget Workflow v6 Training Slides - 2 - Setting Up Joget Workflow
Joget Workflow v6 Training Slides - 2 - Setting Up Joget WorkflowJoget Workflow v6 Training Slides - 2 - Setting Up Joget Workflow
Joget Workflow v6 Training Slides - 2 - Setting Up Joget WorkflowJoget Workflow
 

What's hot (6)

Manipulating Android tasks and back stack
Manipulating Android tasks and back stackManipulating Android tasks and back stack
Manipulating Android tasks and back stack
 
Summit 2015: Mobile App Dev and Content Management with Adobe Experience Manager
Summit 2015: Mobile App Dev and Content Management with Adobe Experience ManagerSummit 2015: Mobile App Dev and Content Management with Adobe Experience Manager
Summit 2015: Mobile App Dev and Content Management with Adobe Experience Manager
 
Hassle-Free Continuous Integration with Real Device Testing
Hassle-Free Continuous Integration with Real Device TestingHassle-Free Continuous Integration with Real Device Testing
Hassle-Free Continuous Integration with Real Device Testing
 
Rohit android lab projects in suresh gyan vihar
Rohit android lab projects in suresh gyan viharRohit android lab projects in suresh gyan vihar
Rohit android lab projects in suresh gyan vihar
 
Let's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UILet's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UI
 
Joget Workflow v6 Training Slides - 2 - Setting Up Joget Workflow
Joget Workflow v6 Training Slides - 2 - Setting Up Joget WorkflowJoget Workflow v6 Training Slides - 2 - Setting Up Joget Workflow
Joget Workflow v6 Training Slides - 2 - Setting Up Joget Workflow
 

Similar to Google i o &amp; android q changes 2019

Lviv MDDay 2014. Ігор Коробка “забезпечення базової безпеки в андроїд аплікац...
Lviv MDDay 2014. Ігор Коробка “забезпечення базової безпеки в андроїд аплікац...Lviv MDDay 2014. Ігор Коробка “забезпечення базової безпеки в андроїд аплікац...
Lviv MDDay 2014. Ігор Коробка “забезпечення базової безпеки в андроїд аплікац...Lviv Startup Club
 
How to fix a bug in production - Rollout.io
How to fix a bug in production - Rollout.ioHow to fix a bug in production - Rollout.io
How to fix a bug in production - Rollout.ioRollout.io
 
Integrating GoogleFit into Android Apps
Integrating GoogleFit into Android AppsIntegrating GoogleFit into Android Apps
Integrating GoogleFit into Android AppsGiles Payne
 
The web - What it has, what it lacks and where it must go - Bulgaria Web Summ...
The web - What it has, what it lacks and where it must go - Bulgaria Web Summ...The web - What it has, what it lacks and where it must go - Bulgaria Web Summ...
The web - What it has, what it lacks and where it must go - Bulgaria Web Summ...Robert Nyman
 
[Slide Deck] - Mobile Observability Internals at Gojek
[Slide Deck] - Mobile Observability Internals at Gojek[Slide Deck] - Mobile Observability Internals at Gojek
[Slide Deck] - Mobile Observability Internals at Gojekraditya gumay
 
Slide Deck - Shift Left Beyond App Performance Improvement at Gojek_.pptx
Slide Deck - Shift Left Beyond App Performance Improvement at Gojek_.pptxSlide Deck - Shift Left Beyond App Performance Improvement at Gojek_.pptx
Slide Deck - Shift Left Beyond App Performance Improvement at Gojek_.pptxraditya gumay
 
AI: Integrate Search Function into Your App Using Bing Search API.
AI: Integrate Search Function into Your App Using Bing Search API.AI: Integrate Search Function into Your App Using Bing Search API.
AI: Integrate Search Function into Your App Using Bing Search API.Marvin Heng
 
Introduction to React for Frontend Developers
Introduction to React for Frontend DevelopersIntroduction to React for Frontend Developers
Introduction to React for Frontend DevelopersSergio Nakamura
 
The web - What it has, what it lacks and where it must go - Istanbul
The web - What it has, what it lacks and where it must go - IstanbulThe web - What it has, what it lacks and where it must go - Istanbul
The web - What it has, what it lacks and where it must go - IstanbulRobert Nyman
 
Android 103 - Firebase and Architecture Components
Android 103 - Firebase and Architecture ComponentsAndroid 103 - Firebase and Architecture Components
Android 103 - Firebase and Architecture ComponentsKai Koenig
 
Hi5 Hackathon Presentation
Hi5 Hackathon PresentationHi5 Hackathon Presentation
Hi5 Hackathon PresentationLou Moore
 
Lecture 12 - Maps, AR_VR_aaaaHardware.pptx
Lecture 12 - Maps, AR_VR_aaaaHardware.pptxLecture 12 - Maps, AR_VR_aaaaHardware.pptx
Lecture 12 - Maps, AR_VR_aaaaHardware.pptxNgLQun
 
Hieu Xamarin iOS9, Android M 3-11-2015
Hieu Xamarin iOS9, Android M  3-11-2015Hieu Xamarin iOS9, Android M  3-11-2015
Hieu Xamarin iOS9, Android M 3-11-2015Nguyen Hieu
 
Search APIs & Universal Links
Search APIs & Universal LinksSearch APIs & Universal Links
Search APIs & Universal LinksYusuke Kita
 
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Frost
 
Google Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification GoogleGoogle Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification GoogleMathias Seguy
 
Skinning Android for Embedded Applications
Skinning Android for Embedded ApplicationsSkinning Android for Embedded Applications
Skinning Android for Embedded ApplicationsVIA Embedded
 
Orangescrum Mobile API Add on User Manual
Orangescrum Mobile API Add on User ManualOrangescrum Mobile API Add on User Manual
Orangescrum Mobile API Add on User ManualOrangescrum
 
Cómo tener analíticas en tu app y no volverte loco
Cómo tener analíticas en tu app y no volverte locoCómo tener analíticas en tu app y no volverte loco
Cómo tener analíticas en tu app y no volverte locoGemma Del Olmo
 

Similar to Google i o &amp; android q changes 2019 (20)

Lviv MDDay 2014. Ігор Коробка “забезпечення базової безпеки в андроїд аплікац...
Lviv MDDay 2014. Ігор Коробка “забезпечення базової безпеки в андроїд аплікац...Lviv MDDay 2014. Ігор Коробка “забезпечення базової безпеки в андроїд аплікац...
Lviv MDDay 2014. Ігор Коробка “забезпечення базової безпеки в андроїд аплікац...
 
How to fix a bug in production - Rollout.io
How to fix a bug in production - Rollout.ioHow to fix a bug in production - Rollout.io
How to fix a bug in production - Rollout.io
 
Integrating GoogleFit into Android Apps
Integrating GoogleFit into Android AppsIntegrating GoogleFit into Android Apps
Integrating GoogleFit into Android Apps
 
The web - What it has, what it lacks and where it must go - Bulgaria Web Summ...
The web - What it has, what it lacks and where it must go - Bulgaria Web Summ...The web - What it has, what it lacks and where it must go - Bulgaria Web Summ...
The web - What it has, what it lacks and where it must go - Bulgaria Web Summ...
 
[Slide Deck] - Mobile Observability Internals at Gojek
[Slide Deck] - Mobile Observability Internals at Gojek[Slide Deck] - Mobile Observability Internals at Gojek
[Slide Deck] - Mobile Observability Internals at Gojek
 
Slide Deck - Shift Left Beyond App Performance Improvement at Gojek_.pptx
Slide Deck - Shift Left Beyond App Performance Improvement at Gojek_.pptxSlide Deck - Shift Left Beyond App Performance Improvement at Gojek_.pptx
Slide Deck - Shift Left Beyond App Performance Improvement at Gojek_.pptx
 
AI: Integrate Search Function into Your App Using Bing Search API.
AI: Integrate Search Function into Your App Using Bing Search API.AI: Integrate Search Function into Your App Using Bing Search API.
AI: Integrate Search Function into Your App Using Bing Search API.
 
Introduction to React for Frontend Developers
Introduction to React for Frontend DevelopersIntroduction to React for Frontend Developers
Introduction to React for Frontend Developers
 
The web - What it has, what it lacks and where it must go - Istanbul
The web - What it has, what it lacks and where it must go - IstanbulThe web - What it has, what it lacks and where it must go - Istanbul
The web - What it has, what it lacks and where it must go - Istanbul
 
Android 103 - Firebase and Architecture Components
Android 103 - Firebase and Architecture ComponentsAndroid 103 - Firebase and Architecture Components
Android 103 - Firebase and Architecture Components
 
Hi5 Hackathon Presentation
Hi5 Hackathon PresentationHi5 Hackathon Presentation
Hi5 Hackathon Presentation
 
Service workers
Service workersService workers
Service workers
 
Lecture 12 - Maps, AR_VR_aaaaHardware.pptx
Lecture 12 - Maps, AR_VR_aaaaHardware.pptxLecture 12 - Maps, AR_VR_aaaaHardware.pptx
Lecture 12 - Maps, AR_VR_aaaaHardware.pptx
 
Hieu Xamarin iOS9, Android M 3-11-2015
Hieu Xamarin iOS9, Android M  3-11-2015Hieu Xamarin iOS9, Android M  3-11-2015
Hieu Xamarin iOS9, Android M 3-11-2015
 
Search APIs & Universal Links
Search APIs & Universal LinksSearch APIs & Universal Links
Search APIs & Universal Links
 
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
 
Google Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification GoogleGoogle Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification Google
 
Skinning Android for Embedded Applications
Skinning Android for Embedded ApplicationsSkinning Android for Embedded Applications
Skinning Android for Embedded Applications
 
Orangescrum Mobile API Add on User Manual
Orangescrum Mobile API Add on User ManualOrangescrum Mobile API Add on User Manual
Orangescrum Mobile API Add on User Manual
 
Cómo tener analíticas en tu app y no volverte loco
Cómo tener analíticas en tu app y no volverte locoCómo tener analíticas en tu app y no volverte loco
Cómo tener analíticas en tu app y no volverte loco
 

More from Vitali Pekelis

Droidkaigi2019thagikura 190208135940
Droidkaigi2019thagikura 190208135940Droidkaigi2019thagikura 190208135940
Droidkaigi2019thagikura 190208135940Vitali Pekelis
 
Advanced #6 clean architecture
Advanced #6  clean architectureAdvanced #6  clean architecture
Advanced #6 clean architectureVitali Pekelis
 
Advanced #4 GPU & Animations
Advanced #4   GPU & AnimationsAdvanced #4   GPU & Animations
Advanced #4 GPU & AnimationsVitali Pekelis
 
Advanced #2 networking
Advanced #2   networkingAdvanced #2   networking
Advanced #2 networkingVitali Pekelis
 
Advanced #1 cpu, memory
Advanced #1   cpu, memoryAdvanced #1   cpu, memory
Advanced #1 cpu, memoryVitali Pekelis
 
All the support you need. Support libs in Android
All the support you need. Support libs in AndroidAll the support you need. Support libs in Android
All the support you need. Support libs in AndroidVitali Pekelis
 
How to build Sdk? Best practices
How to build Sdk? Best practicesHow to build Sdk? Best practices
How to build Sdk? Best practicesVitali Pekelis
 
Android design patterns
Android design patternsAndroid design patterns
Android design patternsVitali Pekelis
 
Advanced #3 threading
Advanced #3  threading Advanced #3  threading
Advanced #3 threading Vitali Pekelis
 
Mobile ui fruit or delicious sweets
Mobile ui  fruit or delicious sweetsMobile ui  fruit or delicious sweets
Mobile ui fruit or delicious sweetsVitali Pekelis
 
Lecture #4 c loaders and co.
Lecture #4 c   loaders and co.Lecture #4 c   loaders and co.
Lecture #4 c loaders and co.Vitali Pekelis
 
Session #4 b content providers
Session #4 b  content providersSession #4 b  content providers
Session #4 b content providersVitali Pekelis
 
Advanced #2 - ui perf
 Advanced #2 - ui perf Advanced #2 - ui perf
Advanced #2 - ui perfVitali Pekelis
 
Android design lecture #3
Android design   lecture #3Android design   lecture #3
Android design lecture #3Vitali Pekelis
 

More from Vitali Pekelis (20)

Droidkaigi2019thagikura 190208135940
Droidkaigi2019thagikura 190208135940Droidkaigi2019thagikura 190208135940
Droidkaigi2019thagikura 190208135940
 
Droidkaigi 2019
Droidkaigi 2019Droidkaigi 2019
Droidkaigi 2019
 
Android Q 2019
Android Q 2019Android Q 2019
Android Q 2019
 
Advanced #6 clean architecture
Advanced #6  clean architectureAdvanced #6  clean architecture
Advanced #6 clean architecture
 
Advanced #4 GPU & Animations
Advanced #4   GPU & AnimationsAdvanced #4   GPU & Animations
Advanced #4 GPU & Animations
 
Advanced #2 networking
Advanced #2   networkingAdvanced #2   networking
Advanced #2 networking
 
Advanced #2 threading
Advanced #2   threadingAdvanced #2   threading
Advanced #2 threading
 
Advanced #1 cpu, memory
Advanced #1   cpu, memoryAdvanced #1   cpu, memory
Advanced #1 cpu, memory
 
All the support you need. Support libs in Android
All the support you need. Support libs in AndroidAll the support you need. Support libs in Android
All the support you need. Support libs in Android
 
How to build Sdk? Best practices
How to build Sdk? Best practicesHow to build Sdk? Best practices
How to build Sdk? Best practices
 
Di &amp; dagger
Di &amp; daggerDi &amp; dagger
Di &amp; dagger
 
Android design patterns
Android design patternsAndroid design patterns
Android design patterns
 
Advanced #3 threading
Advanced #3  threading Advanced #3  threading
Advanced #3 threading
 
Mobile ui fruit or delicious sweets
Mobile ui  fruit or delicious sweetsMobile ui  fruit or delicious sweets
Mobile ui fruit or delicious sweets
 
Lecture #4 c loaders and co.
Lecture #4 c   loaders and co.Lecture #4 c   loaders and co.
Lecture #4 c loaders and co.
 
Session #4 b content providers
Session #4 b  content providersSession #4 b  content providers
Session #4 b content providers
 
Advanced #2 - ui perf
 Advanced #2 - ui perf Advanced #2 - ui perf
Advanced #2 - ui perf
 
Android meetup
Android meetupAndroid meetup
Android meetup
 
Android design lecture #3
Android design   lecture #3Android design   lecture #3
Android design lecture #3
 
From newbie to ...
From newbie to ...From newbie to ...
From newbie to ...
 

Recently uploaded

Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...aditisharan08
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyFrank van der Linden
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptkotipi9215
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number SystemsJheuzeDellosa
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 

Recently uploaded (20)

Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The Ugly
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.ppt
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number Systems
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 

Google i o &amp; android q changes 2019

  • 1. Google IO & Android Q changes by Tim Surkis Hadas Peled Gil Goldzweig 26/06/2019 Wifi Password: NEXT@tlv4ever
  • 4. > 4,000 members Largest Android Active Community
  • 5. What Do We Do? ● Fundamentals ● Advanced ● Design ● Hackathon ● Partner Events ● Mentors Program
  • 6. What Do We Do? ● Fundamentals ● Advanced ● Design ● Hackathon ● Partner Events ● Mentors Program
  • 8.
  • 9. Gil Goldzweig Mobile team lead @ 10bis Github Twitter
  • 10. ● Kotlin ● In-App Updates ● Android Q Changes ○ New navigational system ○ Scoped Storage ○ Location ○ Settings Panel ○ Dark Mode ○ Bubble API ○ Sharing ● Jetpack Security ● Benchmark API ● Jetpack Compose ● CameraX The plan
  • 15. In-App Update immediate in-app updates
  • 16. In-App Update implementation dependencies { implementation 'com.google.android.play:core:1.5.0' }
  • 17. In-App Update implementation private lateinit var updateManager: AppUpdateManager override fun onCreate(savedInstanceState: Bundle?) { updateManager = AppUpdateManagerFactory.create(this) }
  • 18. In-App Update implementation private lateinit var updateManager: AppUpdateManager override fun onCreate(savedInstanceState: Bundle?) { updateManager = AppUpdateManagerFactory.create(this) }
  • 19. In-App Update immediate updates override fun onResume() { updateManager .appUpdateInfo .addOnSuccessListener { updateInfo -> } }
  • 20. In-App Update immediate updates .addOnSuccessListener { updateInfo -> val updateInProgress = updateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS val updateAvailable = updateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && updateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) ... }
  • 21. In-App Update immediate updates .addOnSuccessListener { updateInfo -> val updateInProgress = updateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS val updateAvailable = updateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && updateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) ... }
  • 22. In-App Update immediate updates .addOnSuccessListener { updateInfo -> val updateInProgress = updateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS val updateAvailable = updateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && updateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) ... }
  • 23. In-App Update immediate updates .addOnSuccessListener { updateInfo -> val updateInProgress = /*...*/ val updateAvailable = /*...*/ if (updateInProgress || updateAvailable) { updateManager.startUpdateFlowForResult( updateInfo, AppUpdateType.IMMEDIATE, this, updateRequestCode) } }
  • 24. In-App Update immediate updates .addOnSuccessListener { updateInfo -> val updateInProgress = /*...*/ val updateAvailable = /*...*/ if (updateInProgress || updateAvailable) { updateManager.startUpdateFlowForResult( updateInfo, AppUpdateType.IMMEDIATE, this, updateRequestCode) } }
  • 25. In-App Update immediate updates private val updateRequestCode: Int = 830 override fun onActivityResult( requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { updateRequestCode -> { } } }
  • 26. In-App Update immediate updates updateRequestCode -> { when (resultCode) { Activity.RESULT_OK -> { // Shouldn't occur since the app restarts upon finishing } Activity.RESULT_CANCELED -> { // user canceled or rejected update } ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> { // update failed } } }
  • 27. In-App Update immediate updates updateRequestCode -> { when (resultCode) { Activity.RESULT_OK -> { // Shouldn't occur since the app restarts upon finishing } Activity.RESULT_CANCELED -> { // user canceled or rejected update } ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> { // update failed } } }
  • 28. In-App Update immediate updates updateRequestCode -> { when (resultCode) { Activity.RESULT_OK -> { // Shouldn't occur since the app restarts upon finishing } Activity.RESULT_CANCELED -> { // user canceled or rejected update } ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> { // update failed } } }
  • 29. In-App Update flexible in-app updates
  • 30. In-App Update flexible updates private lateinit var updateManager: AppUpdateManager private lateinit var installListener: InstallStateUpdatedListener override fun onCreate(savedInstanceState: Bundle?) { updateManager = AppUpdateManagerFactory.create(this) installListener = InstallStateUpdatedListener { installState -> } updateManager.registerListener(installListener) }
  • 31. In-App Update flexible updates private lateinit var updateManager: AppUpdateManager private lateinit var installListener: InstallStateUpdatedListener override fun onCreate(savedInstanceState: Bundle?) { updateManager = AppUpdateManagerFactory.create(this) installListener = InstallStateUpdatedListener { installState -> } updateManager.registerListener(installListener) }
  • 32. In-App Update flexible updates private lateinit var updateManager: AppUpdateManager private lateinit var installListener: InstallStateUpdatedListener override fun onCreate(savedInstanceState: Bundle?) { updateManager = AppUpdateManagerFactory.create(this) installListener = InstallStateUpdatedListener { installState -> } updateManager.registerListener(installListener) }
  • 33. In-App Update flexible updates InstallStatus options ● CANCELED ● DOWNLOADED ● DOWNLOADING ● FAILED ● INSTALLED ● INSTALLING ● PENDING ● REQUIRES_UI_INTENT ● UNKNOWN
  • 34. In-App Update flexible updates private lateinit var updateManager: AppUpdateManager private lateinit var installListener: InstallStateUpdatedListener override fun onCreate(savedInstanceState: Bundle?) { updateManager = AppUpdateManagerFactory.create(this) installListener = InstallStateUpdatedListener { installState -> } updateManager.registerListener(installListener) }
  • 35. In-App Update flexible updates override fun onDestroy() { updateManager.unregisterListener(installListener) }
  • 36. In-App Update flexible updates private fun requestUpdate() { updateManager .appUpdateInfo .addOnSuccessListener { updateInfo -> } }
  • 37. In-App Update flexible updates private fun requestUpdate() { updateManager .appUpdateInfo .addOnSuccessListener { updateInfo -> } }
  • 38. In-App Update flexible updates .addOnSuccessListener { updateInfo -> val updateAvailable = updateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE val updateAllowed = updateInfo.isUpdateTypeAllowed( AppUpdateType.FLEXIBLE) if (updateAvailable && updateAllowed) { updateManager.startUpdateFlowForResult( updateInfo, AppUpdateType.FLEXIBLE, activity, updateRequestCode) } }
  • 39. In-App Update flexible updates .addOnSuccessListener { updateInfo -> val updateAvailable = updateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE val updateAllowed = updateInfo.isUpdateTypeAllowed( AppUpdateType.FLEXIBLE) if (updateAvailable && updateAllowed) { updateManager.startUpdateFlowForResult( updateInfo, AppUpdateType.FLEXIBLE, activity, updateRequestCode) } }
  • 40. In-App Update flexible updates .addOnSuccessListener { updateInfo -> val updateAvailable = updateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE val updateAllowed = updateInfo.isUpdateTypeAllowed( AppUpdateType.FLEXIBLE) if (updateAvailable && updateAllowed) { updateManager.startUpdateFlowForResult( updateInfo, AppUpdateType.FLEXIBLE, activity, updateRequestCode) } }
  • 41. In-App Update flexible updates .addOnSuccessListener { updateInfo -> val updateAvailable = updateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE val updateAllowed = updateInfo.isUpdateTypeAllowed( AppUpdateType.FLEXIBLE) if (updateAvailable && updateAllowed) { updateManager.startUpdateFlowForResult( updateInfo, AppUpdateType.FLEXIBLE, activity, updateRequestCode) } }
  • 42. In-App Update flexible updates .addOnSuccessListener { updateInfo -> val updateAvailable = updateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE val updateAllowed = updateInfo.isUpdateTypeAllowed( AppUpdateType.FLEXIBLE) if (updateAvailable && updateAllowed) { updateManager.startUpdateFlowForResult( updateInfo, AppUpdateType.FLEXIBLE, this, updateRequestCode) } }
  • 43. In-App Update flexible updates protected override fun onResume() { updateManager .appUpdateInfo .addOnSuccessListener { updateInfo -> when (updateInfo.installStatus()) { InstallStatus.DOWNLOADED ->{ /*do something*/ } // do something else } } }
  • 44. In-App Update flexible updates protected override fun onResume() { updateManager .appUpdateInfo .addOnSuccessListener { updateInfo -> when (updateInfo.installStatus()) { InstallStatus.DOWNLOADED -> { /*do something*/ } // do something else } } }
  • 45. In-App Update flexible updates protected override fun onResume() { updateManager .appUpdateInfo .addOnSuccessListener { updateInfo -> when (updateInfo.installStatus()) { InstallStatus.DOWNLOADED -> { /*do something*/ } // do something else } } }
  • 47. Navigation history API 5.0 Lollipop API 9.0 Pie API 10.0 Q*******
  • 54. View Gestures Implications override insests MyAPP var exclusionRects = listOf(rect1, rect2) fun onLayout(...) { setSystemGestureExclusionRects(exclusionRects) } fun onDraw(...) { setSystemGestureExclusionRects(exclusionRects) } Rect top bottom rightleft
  • 55. View Gestures Implications override insests MyAPP var exclusionRects = listOf(rect1, rect2, rect3) fun onLayout(...) { setSystemGestureExclusionRects(exclusionRects) } fun onDraw(...) { setSystemGestureExclusionRects(exclusionRects) } Rect top bottom rightleft
  • 56. Gestures Recomendations status and navigation bars MyAPP rootView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE) <!-- values-29/themes.xml: --> <style name="AppTheme" parent="..."> <item name="android:navigationBarColor"> @android:color/transparent</item> <item name="android:statusBarColor"> @android:color/transparent</item> </style>
  • 59. Scoped Storage accessing files <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Api 29+ android Q and higher API 28- android Pie and earlier
  • 60. Scoped Storage accessing app files readwrite MyApp Accessing App Storage Photos MediaStore.Images Videos MediaStore.Video Audio MediaStore.Audio * No permission needed! Other files No access
  • 61. Scoped Storage accessing other app files read Photos MediaStore.Images Videos MediaStore.Video Audio MediaStore.Audio Other files Storage Access Framework downloads DCIM *android.permission.READ_EXTERNAL_STORAGE required
  • 62. Scoped Storage Storage Access Framework readwrite documentation: https://developer.android.com/guide/topics/providers/document-provider Storage Access Framework 98,313 milliseconds Conventional Android File I/O 3,139 millisecondsSource: https://www.xda- developers.com/android-q-storage-access- framework-scoped-storage/
  • 63. Scoped Storage supporting legacy storage <manifest> <application android:requestLegacyExternalStorage="true"> ... </application> </manifest> * Android R will only work with scoped storage
  • 68. When does my app considered to be in the foreground?!
  • 70. Pre - Q <manifest> <!-- ACCESS_COARSE_LOCATION - city level --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <!-- ACCESS_FINE_LOCATION - as best we can do --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> ... </manifest>
  • 71. Targets Q <manifest> <!-- ACCESS_COARSE_LOCATION - city level --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <!-- ACCESS_FINE_LOCATION - as best we can do --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> ... </manifest>
  • 72. Runs on Q - BUT targets 9 or lower Q
  • 73.
  • 75. //Target Q only, request "while-in-use" first ActivityCompat.requestPermissions(this, arrayOf( android.Manifest.permission.ACCESS_COARSE_LOCATION // or FINE ), REQUEST_CODE)
  • 76. //Target Q only, request "while-in-use" first ActivityCompat.requestPermissions(this, arrayOf( android.Manifest.permission.ACCESS_COARSE_LOCATION // or FINE ), REQUEST_CODE) //Request "all-the-time" (only if needed) ActivityCompat.requestPermissions(this, arrayOf( android.Manifest.permission.ACCESS_BACKGROUND_LOCATION ), REQUEST_CODE)
  • 78. if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { //Location accessible in foreground } if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) { //Location accessible in background } App permissions best practices
  • 79. App permissions on OS upgrades Device upgrade scenarios
  • 81. Foreground service requires location type <service android:name="MyNavigationService" android:foregroundServiceType="location" ... > ... </service>
  • 82. If you didn’t declare type location ● Has permission to location ‘while-in-use’ - The service has NO permission for location <service android:name="MyNavigationService" ... > </service>
  • 83. If you didn’t declare type location ● Has permission to location ‘while-in-use’ - The service has NO permission for location ● Has permission to location ‘all-the-time’ - The service has permission for location <service android:name="MyNavigationService" ... > </service>
  • 84.
  • 85. All-the-time permission + fine location in background
  • 86.
  • 87. Examples ● while-in-use: LocationUpdatesForegroundService on GitHub ● all-the-time: LocationUpdatesPendingIntent on GitHub
  • 92. val panelIntent = Intent(Settings.Panel.settings_panel_type) startActivityForResult(panelIntent) Settings panel actions
  • 93. val panelIntent = Intent(Settings.Panel.settings_panel_type) startActivityForResult(panelIntent) ● ACTION_INTERNET_CONNECTIVITY - Airplane mode, Wifi, Mobile Data. Settings panel actions
  • 94. val panelIntent = Intent(Settings.Panel.settings_panel_type) startActivityForResult(panelIntent) ● ACTION_INTERNET_CONNECTIVITY - Airplane mode, Wifi, Mobile Data. ● ACTION_WIFI - Wifi only. Settings panel actions
  • 95. val panelIntent = Intent(Settings.Panel.settings_panel_type) startActivityForResult(panelIntent) ● ACTION_INTERNET_CONNECTIVITY - Airplane mode, Wifi, Mobile Data. ● ACTION_WIFI - Wifi only. ● ACTION_NFC - All settings related to near-field communication (NFC). Settings panel actions
  • 96. val panelIntent = Intent(Settings.Panel.settings_panel_type) startActivityForResult(panelIntent) ● ACTION_INTERNET_CONNECTIVITY - Airplane mode, Wifi, Mobile Data. ● ACTION_WIFI - Wifi only. ● ACTION_NFC - All settings related to near-field communication (NFC). ● ACTION_VOLUME - Volume settings for all audio streams. Settings panel actions
  • 98.
  • 99. Why?
  • 101. Why? Environment Battery Up to 60% reduction in power usage
  • 103. How to enable dark theme: ● Settings -> Display -> Theme
  • 104. How to enable dark theme: ● Settings -> Display -> Theme ● Quick Settings
  • 105. How to enable dark theme: ● Settings -> Display -> Theme ● Quick Settings ● Battery Saver (Pixel devices)
  • 106. Dark theme: app support Strongly recommended that apps will support dark theme.
  • 108. DayNight ● Enables the -night resource qualifier when in dark theme ● Works back to Android 4.0 (API 14) ● Improved in AppCompat v1.1.0
  • 109. <style name="MyTheme" parent="Theme.AppCompat.DayNight"> </style> <!-- OR --> <style name="MyTheme" parent="Theme.MaterialComponents.DayNight"> </style>
  • 114. ModesDayNight MODE_NIGHT_YES //Always use the dark theme MODE_NIGHT_NO //Always use the light theme DEFAULT PRE-Q
  • 115. ModesDayNight MODE_NIGHT_YES //Always use the dark theme MODE_NIGHT_NO //Always use the light theme DEFAULT PRE-Q MODE_NIGHT_FOLLOW_SYSTEM //Follows the current system settings DEFAULT ON Q
  • 116. ModesDayNight MODE_NIGHT_YES //Always use the dark theme MODE_NIGHT_NO //Always use the light theme DEFAULT PRE-Q MODE_NIGHT_FOLLOW_SYSTEM //Follows the current system settings DEFAULT ON Q MODE_NIGHT_AUTO_BATTERY //Dark when battery saver is enabled API 21+ AND AppCompat-v1.1.0
  • 117. Support Night ModeDayNight ● AppCompatDelegate.setDefaultNightMode(mode : Int)
  • 118. Support Night ModeDayNight ● AppCompatDelegate.setDefaultNightMode(mode : Int) ● getDelegate().setLocalNightMode(mode : Int)
  • 119. Support Night ModeDayNight ● AppCompatDelegate.setDefaultNightMode(mode : Int) ● getDelegate().setLocalNightMode(mode : Int) Note: the values are not persisted
  • 120.
  • 121. Force/Smart Dark Automatically convert app to dark theme implementing dark theme
  • 124. Seeing strange things? ● xml - android:forceDarkAllowed="false" ● code - setForceDarkAllowed(false)
  • 125. Force Dark Custom google sample DayNight
  • 129. SAWs* are Dangerous *SYSTEM_ALERT_WINDOW ● Lots of apps are utilizing SAW to do cool things.
  • 130. SAWs* are Dangerous *SYSTEM_ALERT_WINDOW ● Lots of apps are utilizing SAW to do cool things. ● Unfortunately some apps are utilizing SAW for some less cool things.
  • 131.
  • 132. Bubbles ● They float on top of other app content and follow the user
  • 133. Bubbles ● They float on top of other app content and follow the user ● Can be expanded to reveal app functionality and information
  • 134. Bubbles ● They float on top of other app content and follow the user ● Can be expanded to reveal app functionality and information ● Built into the notification system
  • 135. Build a Bubble ● Configure the activity shown in the bubble ● Construct BubbleMetadata ● Add the metadata to your notification and send
  • 136. Configure the activity shown in the bubble <!--Must be embeddable,documentLaunchMode always,and resizable--> <activity android:name=".bubbles.BubbleActivity" ... />
  • 137. Configure the activity shown in the bubble <!--Must be embeddable,documentLaunchMode always,and resizable--> <activity android:name=".bubbles.BubbleActivity" android:allowEmbedded="true" ... />
  • 138. Configure the activity shown in the bubble <!--Must be embeddable,documentLaunchMode always,and resizable--> <activity android:name=".bubbles.BubbleActivity" android:allowEmbedded="true" android:documentLaunchMode="always" ... />
  • 139. Configure the activity shown in the bubble <!--Must be embeddable,documentLaunchMode always,and resizable--> <activity android:name=".bubbles.BubbleActivity" android:allowEmbedded="true" android:documentLaunchMode="always" android:resizeableActivity="true" ... />
  • 140. Construct BubbleMetadata val intent = Intent(mContext, MainActivity::class.java) val bubbleIntent = PendingIntent.getActivity( mContext, 0 /* requestCode */, intent, 0/* flags*/)
  • 141. Construct BubbleMetadata val intent = Intent(mContext, MainActivity::class.java) val bubbleIntent = PendingIntent.getActivity( mContext, 0 /* requestCode */, intent, 0/* flags*/) //Metadata to go on the notification val metadata = Notification.BubbleMetadata.Builder()
  • 142. Construct BubbleMetadata val intent = Intent(mContext, MainActivity::class.java) val bubbleIntent = PendingIntent.getActivity( mContext, 0 /* requestCode */, intent, 0/* flags*/) //Metadata to go on the notification val metadata = Notification.BubbleMetadata.Builder() //Icon is not displayed in Beta Q .setIcon(Icon.createWithResource(mContext, R.drawable.ic_bubble))
  • 143. Construct BubbleMetadata val intent = Intent(mContext, MainActivity::class.java) val bubbleIntent = PendingIntent.getActivity( mContext, 0 /* requestCode */, intent, 0/* flags*/) //Metadata to go on the notification val metadata = Notification.BubbleMetadata.Builder() //Icon is not displayed in Beta Q .setIcon(Icon.createWithResource(mContext, R.drawable.ic_bubble)) .setIntent(bubbleIntent).build()
  • 144. Add the BubbleMetadata to your notification and send var builder = Notification.Builder(mContext, CHANNEL_ID) .setSmallIcon(R.drawable.notification_icon) ... .setBubbleMetadata(metadata)
  • 145. Add the BubbleMetadata to your notification and send var builder = Notification.Builder(mContext, CHANNEL_ID) .setSmallIcon(R.drawable.notification_icon) ... .setBubbleMetadata(metadata) //Send the notification mNotificationManager.notify(BUBBLE_ID, builder.build())
  • 146. When to use Bubbles? ● Messaging ● Calling ● User initiated
  • 148.
  • 149. Sharing ● Content preview - supports images and text
  • 150. Sharing ● Content preview - supports images and text ● New sharing shortcut API - Doesn’t need to start your app
  • 151. Sharing ● Content preview - supports images and text ● New sharing shortcut API - Doesn’t need to start your app ● Copy to clipboard up top
  • 152. Sharing ● Content preview - supports images and text ● New sharing shortcut API - Doesn’t need to start your app ● Copy to clipboard up top ● It’s really really fast
  • 154. Providing a sharing shortcut val sic = mutableListOf<ShortcutInfoCompat>()
  • 155. Providing a sharing shortcut val sic = mutableListOf<ShortcutInfoCompat>() sic.add( ShortcutInfoCompat.Builder(context, personId)
  • 156. Providing a sharing shortcut val sic = mutableListOf<ShortcutInfoCompat>() sic.add( ShortcutInfoCompat.Builder(context, personId) .setShortLabel("Chet")
  • 157. Providing a sharing shortcut val sic = mutableListOf<ShortcutInfoCompat>() sic.add( ShortcutInfoCompat.Builder(context, personId) .setShortLabel("Chet") .setLongLabel("Chet Haase")
  • 158. Providing a sharing shortcut val sic = mutableListOf<ShortcutInfoCompat>() sic.add( ShortcutInfoCompat.Builder(context, personId) .setShortLabel("Chet") .setLongLabel("Chet Haase") .setPerson(...)//Person can improve ranking
  • 159. Providing a sharing shortcut val sic = mutableListOf<ShortcutInfoCompat>() sic.add( ShortcutInfoCompat.Builder(context, personId) .setShortLabel("Chet") .setLongLabel("Chet Haase") .setPerson(...)//Person can improve ranking .setIcon(/* Icon */)
  • 160. Providing a sharing shortcut val sic = mutableListOf<ShortcutInfoCompat>() sic.add( ShortcutInfoCompat.Builder(context, personId) .setShortLabel("Chet") .setLongLabel("Chet Haase") .setPerson(...)//Person can improve ranking .setIcon(/* Icon */) .setCategories(category)//required to be a Sharing Shortcut
  • 161. Providing a sharing shortcut val sic = mutableListOf<ShortcutInfoCompat>() sic.add( ShortcutInfoCompat.Builder(context, personId) .setShortLabel("Chet") .setLongLabel("Chet Haase") .setPerson(...)//Person can improve ranking .setIcon(/* Icon */) .setCategories(category)//required to be a Sharing Shortcut .setIntent(...).build())
  • 162. Providing a sharing shortcut val sic = mutableListOf<ShortcutInfoCompat>() sic.add( ShortcutInfoCompat.Builder(context, personId) .setShortLabel("Chet") .setLongLabel("Chet Haase") .setPerson(...)//Person can improve ranking .setIcon(/* Icon */) .setCategories(category)//required to be a Sharing Shortcut .setIntent(...).build()) ShortcutManagerCompat.addDynamicShortcuts(context, sic)
  • 164. Adding a rich preview content var share = Intent(ACTION_SEND) ...
  • 165. Adding a rich preview content var share = Intent(ACTION_SEND) ... //Metadata must be in intent before Intent#crateChooser share.putExtra(EXTRA_TITLE,"Introducing Android Q Beta")
  • 166. Adding a rich preview content var share = Intent(ACTION_SEND) ... //Metadata must be in intent before Intent#crateChooser share.putExtra(EXTRA_TITLE,"Introducing Android Q Beta") share.clipData = ClipData.newUri(contentResolver, "thumbnail", myContentProviderUri) share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
  • 167. Adding a rich preview content var share = Intent(ACTION_SEND) ... //Metadata must be in intent before Intent#crateChooser share.putExtra(EXTRA_TITLE,"Introducing Android Q Beta") share.clipData = ClipData.newUri(contentResolver, "thumbnail", myContentProviderUri) share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) share = Intent.createChooser(share,null /* no longer used */)
  • 168. Questions ? Queen of Puddings
  • 169.
  • 170. Alpha
  • 172. Jetpack security - key val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
  • 173. Jetpack security - File val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val encryptedFile = EncryptedFile.Builder( ).build()
  • 174. Jetpack security - File val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val encryptedFile = EncryptedFile.Builder( File(context.getFilesDir(), "/your/file"), ).build()
  • 175. Jetpack security - File val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val encryptedFile = EncryptedFile.Builder( File(context.getFilesDir(), "/your/file"), context, ).build()
  • 176. Jetpack security - File val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val encryptedFile = EncryptedFile.Builder( File(context.getFilesDir(), "/your/file"), context, masterKey, ).build()
  • 177. Jetpack security - File val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val encryptedFile = EncryptedFile.Builder( File(context.getFilesDir(), "/your/file"), context, masterKey, EncryptedFileKeyset.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build()
  • 178. Jetpack security - Read File val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val encryptedFile = EncryptedFile.Builder( File(context.getFilesDir(), "/your/file"), context, masterKey, EncryptedFileKeyset.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build() encryptedFile.openInputStream()
  • 179. Jetpack security - Write File val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val encryptedFile = EncryptedFile.Builder( File(context.getFilesDir(), "/your/file"), context, masterKey, EncryptedFileKeyset.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build() encryptedFile.openInputStrem() encryptedFile.openOutputStream()
  • 180. Jetpack security - SharedPreferences
  • 181. Jetpack security - SharedPreferences val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
  • 182. Jetpack security - SharedPreferences val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val sharedPreferences = EncryptedSharedPreferences.create( )
  • 183. Jetpack security - SharedPreferences val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val sharedPreferences = EncryptedSharedPreferences.create( "shareadpreferencesName", )
  • 184. Jetpack security - SharedPreferences val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val sharedPreferences = EncryptedSharedPreferences.create( "shareadpreferencesName", masterKey, )
  • 185. Jetpack security - SharedPreferences val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val sharedPreferences = EncryptedSharedPreferences.create( "shareadpreferencesName", masterKey, context, )
  • 186. Jetpack security - SharedPreferences val masterKey = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) val sharedPreferences = EncryptedSharedPreferences.create( "shareadpreferencesName", masterKey, context, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM )
  • 187. Jetpack security ● Currently in alpha ● Min api 23 ● More information available here
  • 189. Benchmark ● Makes it easy to benchmark your code ● Handles warmup and clock stability for you ● Gives you a accurate result ● Runs as instrumented test
  • 190. What should I benchmark ● RecyclerView scrolling ● Data processing ● Pieces of code that get used repeatedly
  • 194. Benchmark @RunWith(AndroidJUnit4::class) class MyBenchmark() { @get:Rule val rule = BenchmarkRule() @Test fun simpleViewInflate() { } }
  • 195. Benchmark @RunWith(AndroidJUnit4::class) class MyBenchmark() { @get:Rule val rule = BenchmarkRule() @Test fun simpleViewInflate() { val context = ApplicationProvider.getApplicationContext() val inflater = LayoutInflater.from(context) val root = FrameLayout(context) } }
  • 196. Benchmark @RunWith(AndroidJUnit4::class) class MyBenchmark() { @get:Rule val rule = BenchmarkRule() @Test fun simpleViewInflate() { val context = ApplicationProvider.getApplicationContext() val inflater = LayoutInflater.from(context) val root = FrameLayout(context) rule.measureRepeated { inflater.inflate(R.layout.test_simple_view, root, false) } } }
  • 200. Benchmark ● Available as part of AndroidX ● Currently in alpha ● You can learn more about Benchmark here
  • 204.
  • 206. Jetpack compose UI is defined as a function ● Take data as input ● Emits UI hierarchy when invoked @Composable fun Greeting(name: String) { Text("Hello: $name") }
  • 207. @Composable fun Greeting(name: String) { Text("Hello: $name") } Hello: Gil
  • 208. What is compose? ● A set of jetpack UI widgets ● A Kotlin compiler plugin ● Unbundled from platform release ● Uses canvas instead of Views ● Fully compatible with your existing app/code
  • 209. Jetpack compose class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState, persistentState) setContent { Greeting("Android academy") } } }
  • 210. Jetpack compose @Composable fun Greeting(name: String) { Text("Hello: $name") }
  • 211. Jetpack compose - List @Composable fun Greeting(vararg names: String) { Text("Hello: $name") }
  • 212. @Composable fun Greeting(vararg names: String) { List { Text("Hello: $name") } } Jetpack compose - List
  • 213. @Composable fun Greeting(vararg names: String) { List { for (name in names) { Text("Hello: $name") } } } Jetpack compose - List
  • 214. One way data flow Data Events
  • 215. Spinner Item Clicked Value changed OnItemSelected Item Clicked OnItemSelected UI changes Value changed UI changes
  • 220. Jetpack compose @Composable fun Counter() { val amount = state { 0 } }
  • 221. Jetpack compose @Composable fun Counter() { val amount = +state { 0 } }
  • 222. Jetpack compose @Composable fun Counter() { val amount = +state { 0 } Column { } }
  • 223. Jetpack compose @Composable fun Counter() { val amount = +state { 0 } Column { Text("Counter demo") } }
  • 224. Jetpack compose @Composable fun Counter() { val amount = +state { 0 } Column { Text("Counter demo") Button(text = "Add", onClick = { amount.value++ }) Button(text = "Subtract", onClick = { amount.value-- }) } }
  • 225. Jetpack compose @Composable fun Counter() { val amount = +state { 0 } Column { Text("Counter demo") Button(text = "Add", onClick = { amount.value++ }) Button(text = "Subtract", onClick = { amount.value-- }) Text("Clicks: ${amount.value}") } }
  • 226. Jetpack compose ● Currently in pre alpha (Very early stage) ● Available under AOSP and can be used here ● Join the slack channel
  • 230.
  • 231. Easy…. private val captureCallback = object : CameraCaptureSession.CaptureCallback() { private fun process(result: CaptureResult) { when (state) { STATE_PREVIEW -> Unit // Do nothing when the camera preview is working normally. STATE_WAITING_LOCK -> capturePicture(result) STATE_WAITING_PRECAPTURE -> { // CONTROL_AE_STATE can be null on some devices val aeState = result.get(CaptureResult.CONTROL_AE_STATE) if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { state = STATE_WAITING_NON_PRECAPTURE } } STATE_WAITING_NON_PRECAPTURE -> { // CONTROL_AE_STATE can be null on some devices val aeState = result.get(CaptureResult.CONTROL_AE_STATE) if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { state = STATE_PICTURE_TAKEN captureStillPicture() } } } } private fun capturePicture(result: CaptureResult) { val afState = result.get(CaptureResult.CONTROL_AF_STATE) if (afState == null) { captureStillPicture() } else if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) { // CONTROL_AE_STATE can be null on some devices val aeState = result.get(CaptureResult.CONTROL_AE_STATE) if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { state = STATE_PICTURE_TAKEN captureStillPicture() } else { runPrecaptureSequence() } } } override fun onCaptureProgressed(session: CameraCaptureSession, request: CaptureRequest, partialResult: CaptureResult) { process(partialResult) } override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) { process(result) } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? = inflater.inflate(R.layout.fragment_camera2_basic, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { view.findViewById<View>(R.id.picture).setOnClickListener(this) view.findViewById<View>(R.id.info).setOnClickListener(this) textureView = view.findViewById(R.id.texture) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) file = File(activity.getExternalFilesDir(null), PIC_FILE_NAME) } nsor coordinate * @param maxWidth The maximum width that can be chosen * @param maxHeight The maximum height that can be chosen * @param aspectRatio The aspect ratio * @return The optimal `Size`, or an arbitrary one if none were big enough */ @JvmStatic private fun chooseOptimalSize( choices: Array<Size>, textureViewWidth: Int, textureViewHeight: Int, maxWidth: Int, maxHeight: Int, aspectRatio: Size ): Size { // Collect the supported resolutions that are at least as big as the preview Surface val bigEnough = ArrayList<Size>() // Collect the supported resolutions that are smaller than the preview Surface val notBigEnough = ArrayList<Size>() val w = aspectRatio.width val h = aspectRatio.height for (option in choices) { if (option.width <= maxWidth && option.height <= maxHeight && option.height == option.width * h / w) { if (option.width >= textureViewWidth && option.height >= textureViewHeight) { bigEnough.add(option) } else { notBigEnough.add(option) } } } // Pick the smallest of those big enough. If there is no one big enough, pick the // largest of those not big enough. if (bigEnough.size > 0) { return Collections.min(bigEnough, CompareSizesByArea()) } else if (notBigEnough.size > 0) { return Collections.max(notBigEnough, CompareSizesByArea()) } else { Log.e(TAG, "Couldn't find any suitable preview size") return choices[0] } } @JvmStatic fun newInstance(): Camera2BasicFragment = Camera2BasicFragment() } } override fun onResume() { super.onResume() startBackgroundThread() // When the screen is turned off and turned back on, the SurfaceTexture is already // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open // a camera and start preview from here (otherwise, we wait until the surface is ready in // the SurfaceTextureListener). if (textureView.isAvailable) { openCamera(textureView.width, textureView.height) } else { textureView.surfaceTextureListener = surfaceTextureListener } } override fun onPause() { closeCamera() stopBackgroundThread() super.onPause() } private fun requestCameraPermission() { if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { ConfirmationDialog().show(childFragmentManager, FRAGMENT_DIALOG) } else { requestPermissions(arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { if (requestCode == REQUEST_CAMERA_PERMISSION) { if (grantResults.size != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { ErrorDialog.newInstance(getString(R.string.request_permission)) .show(childFragmentManager, FRAGMENT_DIALOG) } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults) } } /** * Sets up member variables related to camera. * * @param width The width of available size for camera preview * @param height The height of available size for camera preview */ private fun setUpCameraOutputs(width: Int, height: Int) { val manager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager try { for (cameraId in manager.cameraIdList) { val characteristics = manager.getCameraCharacteristics(cameraId) // We don't use a front facing camera in this sample. val cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING) if (cameraDirection != null && cameraDirection == CameraCharacteristics.LENS_FACING_FRONT) { continue } val map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue // For still image captures, we use the largest available size. val largest = Collections.max( Arrays.asList(*map.getOutputSizes(ImageFormat.JPEG)), CompareSizesByArea()) imageReader = ImageReader.newInstance(largest.width, largest.height, ImageFormat.JPEG, /*maxImages*/ 2).apply { setOnImageAvailableListener(onImageAvailableListener, backgroundHandler) } // Find out if we need to swap dimension to get the preview size relative to sensor // coordinate. val displayRotation = activity.windowManager.defaultDisplay.rotation sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) val swappedDimensions = areDimensionsSwapped(displayRotation) val displaySize = Point() activity.windowManager.defaultDisplay.getSize(displaySize) val rotatedPreviewWidth = if (swappedDimensions) height else width val rotatedPreviewHeight = if (swappedDimensions) width else height var maxPreviewWidth = if (swappedDimensions) displaySize.y else displaySize.x var maxPreviewHeight = if (swappedDimensions) displaySize.x else displaySize.y if (maxPreviewWidth > MAX_PREVIEW_WIDTH) maxPreviewWidth = MAX_PREVIEW_WIDTH if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) maxPreviewHeight = MAX_PREVIEW_HEIGHT // Danger, W.R.! Attempting to use too large a preview size could exceed the camera // bus' bandwidth limitation, resulting in gorgeous previews but the storage of // garbage capture data. previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture::class.java), rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, maxPreviewHeight, largest) // We fit the aspect ratio of TextureView to the size of preview we picked. if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { textureView.setAspectRatio(previewSize.width, previewSize.height) } else { textureView.setAspectRatio(previewSize.height, previewSize.width) } // Check if the flash is supported. flashSupported = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true val captureBuilder = cameraDevice?.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE)?.apply { addTarget(imageReader?.surface) // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X) // We have to take that into account and rotate JPEG properly. // For devices with orientation of 90, we return our mapping from ORIENTATIONS. // For devices with orientation of 270, we need to rotate the JPEG 180 degrees. set(CaptureRequest.JPEG_ORIENTATION, (ORIENTATIONS.get(rotation) + sensorOrientation + 270) % 360) // Use the same AE and AF modes as the preview. set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) }?.also { setAutoFlash(it) } val captureCallback = object : CameraCaptureSession.CaptureCallback() { override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) { activity.showToast("Saved: $file") Log.d(TAG, file.toString()) unlockFocus() } } captureSession?.apply { stopRepeating() abortCaptures() capture(captureBuilder?.build(), captureCallback, null) } } catch (e: CameraAccessException) { Log.e(TAG, e.toString()) } } /** * Unlock the focus. This method should be called when still image capture sequence is * finished. */ private fun unlockFocus() { try { // Reset the auto-focus trigger previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL) setAutoFlash(previewRequestBuilder) captureSession?.capture(previewRequestBuilder.build(), captureCallback, backgroundHandler) // After this, the camera will go back to the normal state of preview. state = STATE_PREVIEW captureSession?.setRepeatingRequest(previewRequest, captureCallback, backgroundHandler) } catch (e: CameraAccessException) { Log.e(TAG, e.toString()) } } override fun onClick(view: View) { when (view.id) { R.id.picture -> lockFocus() R.id.info -> { if (activity != null) { AlertDialog.Builder(activity) .setMessage(R.string.intro_message) .setPositiveButton(android.R.string.ok, null) .show() } } } } private fun setAutoFlash(requestBuilder: CaptureRequest.Builder) { if (flashSupported) { requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH) } } companion object { /** * Conversion from screen rotation to JPEG orientation. */ private val ORIENTATIONS = SparseIntArray() private val FRAGMENT_DIALOG = "dialog" init { ORIENTATIONS.append(Surface.ROTATION_0, 90) ORIENTATIONS.append(Surface.ROTATION_90, 0) ORIENTATIONS.append(Surface.ROTATION_180, 270) ORIENTATIONS.append(Surface.ROTATION_270, 180) } /** * Tag for the [Log]. */ private val TAG = "Camera2BasicFragment" /** * Camera state: Showing camera preview. */ private val STATE_PREVIEW = 0 /** * Camera state: Waiting for the focus to be locked. */ private val STATE_WAITING_LOCK = 1 /** * Camera state: Waiting for the exposure to be precapture state. */ private val STATE_WAITING_PRECAPTURE = 2 /** * Camera state: Waiting for the exposure state to be something other than precapture. */ private val STATE_WAITING_NON_PRECAPTURE = 3 /** * Camera state: Picture was taken. */ private val STATE_PICTURE_TAKEN = 4 /** * Max preview width that is guaranteed by Camera2 API */ private val MAX_PREVIEW_WIDTH = 1920 /** * Max preview height that is guaranteed by Camera2 API */ private val MAX_PREVIEW_HEIGHT = 1080 /** this.cameraId = cameraId // We've found a viable camera and finished setting up member variables, // so we don't need to iterate through other available cameras. return } } catch (e: CameraAccessException) { Log.e(TAG, e.toString()) } catch (e: NullPointerException) { // Currently an NPE is thrown when the Camera2API is used but not supported on the // device this code runs. ErrorDialog.newInstance(getString(R.string.camera_error)) .show(childFragmentManager, FRAGMENT_DIALOG) } } /** * Determines if the dimensions are swapped given the phone's current rotation. * * @param displayRotation The current rotation of the display * * @return true if the dimensions are swapped, false otherwise. */ private fun areDimensionsSwapped(displayRotation: Int): Boolean { var swappedDimensions = false when (displayRotation) { Surface.ROTATION_0, Surface.ROTATION_180 -> { if (sensorOrientation == 90 || sensorOrientation == 270) { swappedDimensions = true } } Surface.ROTATION_90, Surface.ROTATION_270 -> { if (sensorOrientation == 0 || sensorOrientation == 180) { swappedDimensions = true } } else -> { Log.e(TAG, "Display rotation is invalid: $displayRotation") } } return swappedDimensions } /** * Opens the camera specified by [Camera2BasicFragment.cameraId]. */ private fun openCamera(width: Int, height: Int) { val permission = ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) if (permission != PackageManager.PERMISSION_GRANTED) { requestCameraPermission() return } setUpCameraOutputs(width, height) configureTransform(width, height) val manager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager try { // Wait for camera to open - 2.5 seconds is sufficient if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { throw RuntimeException("Time out waiting to lock camera opening.") } manager.openCamera(cameraId, stateCallback, backgroundHandler) } catch (e: CameraAccessException) { Log.e(TAG, e.toString()) } catch (e: InterruptedException) { throw RuntimeException("Interrupted while trying to lock camera opening.", e) } } /** * Closes the current [CameraDevice]. */ private fun closeCamera() { try { cameraOpenCloseLock.acquire() captureSession?.close() captureSession = null cameraDevice?.close() cameraDevice = null imageReader?.close() imageReader = null } catch (e: InterruptedException) { throw RuntimeException("Interrupted while trying to lock camera closing.", e) } finally { cameraOpenCloseLock.release() } } /** * Starts a background thread and its [Handler]. */ private fun startBackgroundThread() { backgroundThread = HandlerThread("CameraBackground").also { it.start() } backgroundHandler = Handler(backgroundThread?.looper) } /** * Stops the background thread and its [Handler]. hen we get a response in * [.captureCallback] from both [.lockFocus]. */ private fun captureStillPicture() { try { if (activity == null || cameraDevice == null) return val rotation = activity.windowManager.defaultDisplay.rotation // This is the CaptureRequest.Builder that we use to take a picture. e
  • 233. Camera X ● Simplified usage ● Solve many bugs and pain points ● Consistency across devices
  • 234. Simplified usage ● Preview ● Image analysis ● Image capture
  • 236. Preview val previewConfig = PreviewConfig.Builder().build()
  • 237. Preview val previewConfig = PreviewConfig.Builder().build() val preview = Preview(previewConfig)
  • 238. Preview val previewConfig = PreviewConfig.Builder().build() val preview = Preview(previewConfig) preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> /* Your code here. * For example, use previewOutput?.getSurfaceTexture() * and post to a GL renderer. */ }
  • 239. Preview val previewConfig = PreviewConfig.Builder().build() val preview = Preview(previewConfig) preview.setOnPreviewOutputUpdateListener { previewOutput: Preview.PreviewOutput? -> /* Your code here. * For example, use previewOutput?.getSurfaceTexture() * and post to a GL renderer. */ } CameraX.bindToLifecycle(this as LifecycleOwner, preview)
  • 242. Image analysis val imageAnalysisConfig = ImageAnalysisConfig.Builder()
  • 243. Image analysis val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720))
  • 244. Image analysis val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720)) .build()
  • 245. Image analysis val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720)) .build() val imageAnalysis = ImageAnalysis(imageAnalysisConfig)
  • 246. Image analysis val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720)) .build() val imageAnalysis = ImageAnalysis(imageAnalysisConfig) imageAnalysis.setAnalyzer { image: ImageProxy, rotationDegrees: Int -> //insert your analysis code here }
  • 247. Image analysis val imageAnalysisConfig = ImageAnalysisConfig.Builder() .setTargetResolution(Size(1280, 720)) .build() val imageAnalysis = ImageAnalysis(imageAnalysisConfig) imageAnalysis.setAnalyzer { image: ImageProxy, rotationDegrees: Int -> //insert your analysis code here } CameraX.bindToLifecycle(this as LifecycleOwner, imageAnalysis, preview)
  • 249. Image capture val imageCaptureConfig = ImageCaptureConfig.Builder()
  • 250. Image capture val imageCaptureConfig = ImageCaptureConfig.Builder() .setTargetRotation(windowManager.defaultDisplay.rotation)
  • 251. Image capture val imageCaptureConfig = ImageCaptureConfig.Builder() .setTargetRotation(windowManager.defaultDisplay.rotation) .build()
  • 252. Image capture val imageCaptureConfig = ImageCaptureConfig.Builder() .setTargetRotation(windowManager.defaultDisplay.rotation) .build() val imageCapture = ImageCapture(imageCaptureConfig)
  • 253. Image capture val imageCaptureConfig = ImageCaptureConfig.Builder() .setTargetRotation(windowManager.defaultDisplay.rotation) .build() val imageCapture = ImageCapture(imageCaptureConfig) CameraX.bindToLifecycle(this as LifecycleOwner, imageCapture, imageAnalysis, preview)
  • 254. Save an Image on user action fun onClick() { }
  • 255. fun onClick() { val file = File(...) } Save an Image on user action
  • 256. fun onClick() { val file = File(...) imageCapture.takePicture( ) } Save an Image on user action
  • 257. fun onClick() { val file = File(...) imageCapture.takePicture(file, ) } Save an Image on user action
  • 258. fun onClick() { val file = File(...) imageCapture.takePicture(file, object : ImageCapture.OnImageSavedListener { override fun onError(error: ImageCapture.UseCaseError, message: String, exc: Throwable?) { // insert your code here. } override fun onImageSaved(file: File) { // insert your code here. } }) } Listen to save result
  • 260. Extensions val builder = ImageCaptureConfig.Builder()
  • 261. Extensions val builder = ImageCaptureConfig.Builder() val bokehImageCapture = BokehImageCaptureExtender.create(builder)
  • 262. Extensions val builder = ImageCaptureConfig.Builder() val bokehImageCapture = BokehImageCaptureExtender.create(builder) if (bokehImageCapture.isExtensionAvailable()) { bokehImageCapture.enableExtension() }
  • 263. CameraX ● Currently in alpha ● Available under AndroidX ● You can read more here
  • 264.

Editor's Notes

  1. מידע על מיקום נותן את התכנים השימושיים, המעשירים והמרגשים ביותר למשתמשים - עזרה בניווט ממקום למקום, מציאת מסעדות וחנויות ועוד. .
  2. לפני אנדרואיד קיו הרשאת מיקום היה בעל ערך בינארי -או שכן או שלא. אם יוזר מסכים לתת לאפליקציה הרשאה למיקום, האפליקציה יכולה לעקוב אחר המיקום גם כשהיוזר משתמש באפליקציה וגם כשהוא לא.
  3. באנדרואיד קיו נוספת ליוזר אופציה חדשה - אשר רק כשאני משתמש באפליקציה. אם היוזר בוחר באופציה הזאת - האפליקציה רשאית לגשת למיקום רק כשהאפליקציה בפורגראונד.
  4. אז מתי האפליקצייה שלי נחשבת בפורגראונד
  5. אוקי, אז ישנם שני מקרים : המקרה הראשון הוא כשהאפליקציה פתוחה והיוזר רואה אותה על המסך, פורגראונד אקטיביטי, והשני הוא כשהאפליקציה מריצה פורגראונד סרויס. בגדול פורגראונד סרוויס זה סרוויס רגיל, אבל במידה והאפליקציה סגורה או לא מוצגת במסך נוצרת נוטיפיקציה כך שהיוזר יודע שעדיין מתרחש איזשהו תהליך באפליקציה. דוגמה לשימוש בפורגראונד סרוויס - ניווט באפליקציית גוגל מפס, אם היוזר יוצא מהאפליקציה או סוגר אותה במהלך ניווט, האפליקציה ממשיכה להכווין את המשתמש דרך נוטיפיקציה. אוקיי, אז במהלך הדיבור על הרשאות מיקום כל פעם שאגיד , אפליקצייה בפורגראונד, הכוונה היא או שיש אקטיביטי בפורגראונד או שיש פורגראונד סרוויס, אחרת האפליקציה באקגראונד.
  6. .באנדרואיד קיו, אם האפליקציה שלכם מוכוונת לקיו אס די קיי, צריך להוסיף הצהרה לשימוש בלוקשיין בבאקגראונד. אקסס קורס לוקשיין ופיין לוקשיין יאפשרו לאפליקציה לגשת למיקום רק כשהיא בפורגראונד.
  7. .באנדרואיד קיו, אם האפליקציה שלכם מוכוונת לקיו אס די קיי, צריך להוסיף הצהרה לשימוש בלוקשיין בבאקגראונד. אקסס קורס לוקשיין ופיין לוקשיין יאפשרו לאפליקציה לגשת למיקום רק כשהיא בפורגראונד.
  8. אם האפליקצייה לא מוכוונת לקיו אס די קיי אבל רצה על אנדראויד קיו - המערכת מוסיפה את הבאקגראונד פרמישיין אוטומטית,.
  9. אחד היתרונות לטרגט את אנדרואיד קיו , הוא ההפרדה בין בקשות לפורגראונד ולבאקגראונד. ככה ליוזר יהיה קל יותר להבין למה הוא צריך לאפשר שימוש במיקום והסיכוי שהוא יתן לכם את האישור יגדל. בתמונה ניתן לראות דוגמה לדיאלוג לבקשת שימוש במיקום בבאקגראונד אחרי שהיוזר כבר אישר שימוש בפורגראונד.
  10. אם האפליקצייה לא מוכוונת לקיו אס די קיי אבל רצה על אנדראויד קיו - המערכת מוסיפה את הבאקגראונד פרמישיין אוטומטית,.
  11. .לבקשת הרשאת מיקום בבאקגראונד, מומלץ קודם לבדוק אם קיים אישור לשימוש בפורגראונד ואם יש אז לבקש רק את ההרשאה הנוספת
  12. .לבקשת הרשאת מיקום בבאקגראונד, מומלץ קודם לבדוק אם קיים אישור לשימוש בפורגראונד ואם יש אז לבקש רק את ההרשאה הנוספת
  13. בדיקת הרשאה למיקום בבאקגראונד מאד דומה, פשוט צריך לבדוק אם יש פרמישיין לאקסס באקגראונד לוקשיין
  14. בדיקת הרשאה למיקום בבאקגראונד מאד דומה, פשוט צריך לבדוק אם יש פרמישיין לאקסס באקגראונד לוקשיין
  15. מה קורה אם יוזר הוריד את האפליקצייה על אנדרואיד פיי ואחר כך עדכן לאנדרואיד קיו? אם היוזר נתן הרשאות למיקום אז באופן אוטומטי יהיה לאפליקציה גם הרשאה למיקום בבאקגראונד, כך שלא יהיה שינוי התנהגות באפליקציה.
  16. מה קורה אם יוזר הוריד את האפליקצייה על אנדרואיד פיי ואחר כך עדכן לאנדרואיד קיו? אם היוזר נתן הרשאות למיקום אז באופן אוטומטי יהיה לאפליקציה גם הרשאה למיקום בבאקגראונד, כך שלא יהיה שינוי התנהגות באפליקציה.
  17. מאנדרואיד קיו אפליקציות שצריכות לקבל מיקום מתוך פורגראונד סרוויס צריכות להצהיר שזהו סרוויס מסוג לוקשיין במניפסט.
  18. אם לאפליקצייה יש אישור לשימוש במיקום בבאדגראונד לסרוויס יהיה גישה למיקום. השינויים האלה הם עבור כל האפליקציות ללא התחשבות בטרגט אס די קיי. במילים אחרות - אפליקצייה צריכה להצהיר לוקיישן טייפ אם היא רוצה להשתמש במיקום בפורגאונדסרויס וגם לבקש מהיוזר שימוש במיקום פורגראונד בלבד
  19. אם לאפליקצייה יש אישור לשימוש במיקום בבאדגראונד לסרוויס יהיה גישה למיקום. השינויים האלה הם עבור כל האפליקציות ללא התחשבות בטרגט אס די קיי. במילים אחרות - אפליקצייה צריכה להצהיר לוקיישן טייפ אם היא רוצה להשתמש במיקום בפורגאונדסרויס וגם לבקש מהיוזר שימוש במיקום פורגראונד בלבד
  20. כשהאפליקציה ניגשת למיקום מהבאקגראונד, תשלח נוטיפיקציה מהמערכת שתתריע ליוזר שהאפליקציה משתמשת עכשיו במקום.
  21. יוזר יקבל באופן חד-פעמי פר-אפליקציה כשהיוזר מעניק לאפליקציה הרשאה לבאקגראטנד יחד עם פיין לוקישיין. עם הוא עושה שינויים בהרשאות הוא יוכל לקבל את הנוטיפיקציה הזאת שוב
  22. כשיוזר לוחץ על הנוטיפיקציה יפתח מסך הרשאות המיקום של האפליקציה שם הוא יוכל לשנות את ההרשאות למיקום רק בפורגראונד או לחסום לגמרי את האפליקציה ממידע על המיקום.
  23. לפני אנדראויד קיו, אם יוזר במצב טיסה פותח אפליקציה שצריכה אינטרנט, המקסימום שהאפליקציה יכלה לעשות זה להציג לו דיאלוג שלחיצה עליו תפתח את הסטינגנס של המכשיר.
  24. היוזר יכול לשנות הגדרות מבלי לעזוב את האפליקציה, להפעיל וויפי ולבחור רשת..
  25. הגדרות שמע
  26. הגדרות שמע
  27. הגדרות שמע
  28. הגדרות שמע
  29. הגדרות שמע
  30. הגדרות שמע
  31. ובעתיד גוגל מתכוונים לעשות ווראפר לכל זה שיפתח את העמוד בסטיגנס המתאים ביותר. ועכשיו נעבור לדבר על דארק ט’ים
  32. דארק ט’ים
  33. אז למה דארק ט’ים חשוב?
  34. אז דבר ראשון תמיכה בשימוש באפליקציה במקומות חשוכים, כמה פעמים יצא לכם להשתמש בפלאפון בחדר חשוך ולהסתנוור מהמסך?
  35. בנוסף, הארת פחות פיקסלים במסך מפחית את צריכת הסוללה משמעותית.למעשה בדיקות בגוגל מצאו כי דארק ט’ים חוסך עד 60% מצריכת הסוללה כשהאפליקציה בפורגראונד.
  36. ואחרון, נגישות, דארק ט’ים פחות מאמץ את העיניים ולהרבה אנשים שיותר נוח
  37. אז יש כמה דרכים להפעיל דארק ט’ים באנדרואיד קיו - האופציה הראשונה היא דרך הדיספליי סטינגס
  38. אופציה שנייה דרך הקוייק סטינגס
  39. והאופציה השלישית דרך מצב בטרי סייבר במכשירי פיקסל .
  40. חשוב להבין כי מעבר לדארק ט’ים זה לא רק מעבר של המערכת ליו איי תואם אלה זה נוגע בכל האפליקציות. יוזרים יצפו שהאפליקציה שלכם תתנהג בהתאם ולכן חשוב לתמוך בדארק ט’ים
  41. דיינייט הוא פיצ’ר באפקומפט שעוזר לנו ליישם בקלות דארקט’ים באפליקציה. הוא יקח רסורסים מהנייט קווליפייר בדארק ט’ם. עובד מאנדרואיד 4.0. ושופר אפקומפט 1.1
  42. דיינייט הוא פיצ’ר באפקומפט שעוזר לנו ליישם בקלות דארקט’ים באפליקציה. הוא יקח רסורסים מהנייט קווליפייר בדארק ט’ם. עובד מאנדרואיד 4.0. ושופר אפקומפט 1.1
  43. בשביל להשתמש בדיינייט הדבר הראשון שצריך לעשות הוא לשנות את הפרנט של הט’ים שלכם לאחד הט’ימים של דיינייט Google recommend using Material Design Components, since its color theming system (such as the theme attributes ?attr/colorSurface and ?attr/colorOnSurface) provides easy access to suitable colors.
  44. כפי שציינתי קודם אפקומפט מאפשרת ליצור ריסורס קווליפייר של נייט. ככה בדיוק הט’ים מיושם, בלייט ט’ים יש דוט לייט ובנייט ט’ים ייושם הט’ים הכהה..
  45. כפי שציינתי קודם אפקומפט מאפשרת ליצור ריסורס קווליפייר של נייט. ככה בדיוק הט’ים מיושם, בלייט ט’ים יש דוט לייט ובנייט ט’ים ייושם הט’ים הכהה..
  46. כפי שציינתי קודם אפקומפט מאפשרת ליצור ריסורס קווליפייר של נייט. ככה בדיוק הט’ים מיושם, בלייט ט’ים יש דוט לייט ובנייט ט’ים ייושם הט’ים הכהה..
  47. נייט אוטו- באטרי -דארקמוד מופעל בכל פעם שבטרי סייבר פועל מאייפיאי 21
  48. נייט אוטו- באטרי -דארקמוד מופעל בכל פעם שבטרי סייבר פועל מאייפיאי 21
  49. נייט אוטו- באטרי -דארקמוד מופעל בכל פעם שבטרי סייבר פועל מאייפיאי 21
  50. נייט אוטו- באטרי -דארקמוד מופעל בכל פעם שבטרי סייבר פועל מאייפיאי 21
  51. הדבר הבא שצריך לעשות הוא להגיד לאפקומפט באיזה מוד להשתמש. יש לנו שני אייפיי’ס לזה, הראשון סטדיפולט נייט מוד. הוא משמש כשרוצים להגדיר מוד עבור כל האפליקציה, כל האקטיביטיס. אם רוצים להגדיר מוד עבור אקטיביטי מסויים ניתן להשתמש בסט לוקל נייט מוד
  52. הדבר הבא שצריך לעשות הוא להגיד לאפקומפט באיזה מוד להשתמש. יש לנו שני אייפיי’ס לזה, הראשון סטדיפולט נייט מוד. הוא משמש כשרוצים להגדיר מוד עבור כל האפליקציה, כל האקטיביטיס. אם רוצים להגדיר מוד עבור אקטיביטי מסויים ניתן להשתמש בסט לוקל נייט מוד
  53. In activity - call prior to super() in onCreate()
  54. דארק ט’ים זה אחלה.. אבל נשמע כמו ימבה עבודה
  55. We won’t know exactly how this feature works until the source code for Android Q is released or Google publishes documentation for it, though.
  56. בשביל להפעיל פורס דארק צריך להוסיף פורס דארק לטרו. בדוגמה כאן אני מפעילה פורס דארק בט’ים של האפליקציה ככה הוא יופעל בכל מקום באפליקציה. אבל אפשר לבחור איפה להפעיל אותו, ככה שאפשר להפעיל אותו רק עבור אקטיבי אחד או שניים.
  57. כדאי לבדוק טוב טוב את האפליקציה לפני שמפעילים פורס דארק על כל האפליקציה. אם רואים דברים מוזרים אז פשוט משתמשים בפונקציות האלה וזה יסדר אתכם.
  58. כדאי לבדוק טוב טוב את האפליקציה לפני שמפעילים פורס דארק על כל האפליקציה. אם רואים דברים מוזרים אז פשוט משתמשים בפונקציות האלה וזה יסדר אתכם.
  59. הבאסה היא שפורסדארק עובד רק מאנדרואיד קיו. לכן כדאי לתמוך בדארק ט’ים ע”י יצירת קוסטום ט’ים. סיבה נוספת לייצירת קוסטום ט’ים היא שעם פורסדארק אין לנו שליטה איך המסך יראה
  60. הדבר הבא שצריך לעשות הוא להגיד לאפקומפט באיזה מוד להשתמש. יש לנו שני אייפיי’ס לזה, הראשון סטדיפולט נייט מוד. הוא משמש כשרוצים להגדיר מוד עבור כל האפליקציה, כל האקטיביטיס. אם רוצים להגדיר מוד עבור אקטיביטי מסויים ניתן להשתמש בסט לוקל נייט מוד
  61. בבלס הוא אלטרנטיבה לסיסטם אלרט ווינדו. הוא חדש בקיו כדבלופר פרויו, ניתן למצוא את זה בדוולופר אופשיינס. בגרסת בטה זה יפעל אוטומטית..
  62. יכולים להתרחב ולחשוף פונקציונליות ומידע, ולהתכווץ חזרה. המסך המורחב זה בעצם אקטיביטי
  63. יכולים להתרחב ולחשוף פונקציונליות ומידע, ולהתכווץ חזרה. המסך המורחב זה בעצם אקטיביטי
  64. יכולים להתרחב ולחשוף פונקציונליות ומידע, ולהתכווץ חזרה. המסך המורחב זה בעצם אקטיביטי
  65. אז בשביל בניית בבל צריך : אחד, לבנות את האקטיביטי שמוצגת בבבל. שתיים לבנות בבלמטאדאטה שלוש, להוסיף את המטאדאטה לנוטיפיקציה ולשלוח
  66. אז דבר ראשון צריך אקטיביטי שתהיה המצב האקספנדד של הבבל. ישנם שלושה דברים שחייבים להיות לאקטיביטי בשביל שהיא תהיה בבל - אמבדבל, דוקומנטלונצ’ מוד תמיד ורסייזאבל. אם אחד מאלה חסר אז הבבל יהיה פשוט נוטיפיקציה רגילה.
  67. אז דבר ראשון צריך אקטיביטי שתהיה המצב האקספנדד של הבבל. ישנם שלושה דברים שחייבים להיות לאקטיביטי בשביל שהיא תהיה בבל - אמבדבל, דוקומנטלונצ’ מוד תמיד ורסייזאבל. אם אחד מאלה חסר אז הבבל יהיה פשוט נוטיפיקציה רגילה.
  68. אז דבר ראשון צריך אקטיביטי שתהיה המצב האקספנדד של הבבל. ישנם שלושה דברים שחייבים להיות לאקטיביטי בשביל שהיא תהיה בבל - אמבדבל, דוקומנטלונצ’ מוד תמיד ורסייזאבל. אם אחד מאלה חסר אז הבבל יהיה פשוט נוטיפיקציה רגילה.
  69. אז דבר ראשון צריך אקטיביטי שתהיה המצב האקספנדד של הבבל. ישנם שלושה דברים שחייבים להיות לאקטיביטי בשביל שהיא תהיה בבל - אמבדבל, דוקומנטלונצ’ מוד תמיד ורסייזאבל. אם אחד מאלה חסר אז הבבל יהיה פשוט נוטיפיקציה רגילה.
  70. ואז יוצרים בבל מטאדאטה עם האייפיאי החדש. כל מה שצריך זה אייקון ואת הפנדינג אינטנט.
  71. ואז יוצרים בבל מטאדאטה עם האייפיאי החדש. כל מה שצריך זה אייקון ואת הפנדינג אינטנט.
  72. ואז יוצרים בבל מטאדאטה עם האייפיאי החדש. כל מה שצריך זה אייקון ואת הפנדינג אינטנט.
  73. ואז יוצרים בבל מטאדאטה עם האייפיאי החדש. כל מה שצריך זה אייקון ואת הפנדינג אינטנט.
  74. ואז בונים נוטיפיקציה כרגיל ורק מוסיפים את הבבל מטאדטה
  75. ואז בונים נוטיפיקציה כרגיל ורק מוסיפים את הבבל מטאדטה
  76. Q BETA 2
  77. קצת פירוט על הכללים -
  78. קצת פירוט על הכללים -
  79. When a bubble is expanded, the content activity goes through the normal process lifecycle, resulting in the application becoming a foreground process (if not already). When the bubble is collapsed or dismissed the activity will be destroyed. This may result in the process being cached and later killed, depending on whether the app has any other foreground components running.
  80. .אז אני רוצה לנצל עכשיו רגע בשביל לשתף אתכם בחוויה להיות בגוגל איי או, פסטיבל ענק עם האנשים שלימדו אותי אנדרואיד בישראל, ועם האנשים שלימדו אותי אנדרואיד באינטרנט. היה פשוט אדיר.השנה בגוגל איי או גוגל שמו דגש מיוחד על הצורך והחשיבות בשיתוף .
  81. גוגל מציגים שיירים שיט חדש. המכיל קונטנטפרוי - מידע על התוכן אותו רוצים לשתף, אפשר להוסיף תמונה ו/או טקסט
  82. איי פי איי חדש שיירינג שורטקאטטס
  83. העתק לקליפבורד
  84. ושיפור ביצועים
  85. אז קודם כל נדבר על האייפי איי החדש לשיירינג שורטקאט, בדוגמה כאן רואים 4 קיצורים אבל השרייניג שיט החדש תומך בעד 8 קיצורים חדשים
  86. בשביל ליצור שיירינגשורטקאט צריך לבנות קישור דינאמי כרגיל, עם תוספת של שדה קטגוריה (חובה), וכדאי גם להוסיף שדה של פרסן. שיירינג שורטקאט חייב להיות מקושר עם אקטיביטי שתואמת את הקטגוריה , והאקטיביטי חייבת להיות בעלת אקשיין סנד אינטנט פילטר(מה שכבר קיים אם האקטיביטי שלכם כבר בשייר שיט)
  87. בשביל ליצור שיירינגשורטקאט צריך לבנות קישור דינאמי כרגיל, עם תוספת של שדה קטגוריה (חובה), וכדאי גם להוסיף שדה של פרסן. שיירינג שורטקאט חייב להיות מקושר עם אקטיביטי שתואמת את הקטגוריה , והאקטיביטי חייבת להיות בעלת אקשיין סנד אינטנט פילטר(מה שכבר קיים אם האקטיביטי שלכם כבר בשייר שיט)
  88. בשביל ליצור שיירינגשורטקאט צריך לבנות קישור דינאמי כרגיל, עם תוספת של שדה קטגוריה (חובה), וכדאי גם להוסיף שדה של פרסן. שיירינג שורטקאט חייב להיות מקושר עם אקטיביטי שתואמת את הקטגוריה , והאקטיביטי חייבת להיות בעלת אקשיין סנד אינטנט פילטר(מה שכבר קיים אם האקטיביטי שלכם כבר בשייר שיט)
  89. בשביל ליצור שיירינגשורטקאט צריך לבנות קישור דינאמי כרגיל, עם תוספת של שדה קטגוריה (חובה), וכדאי גם להוסיף שדה של פרסן. שיירינג שורטקאט חייב להיות מקושר עם אקטיביטי שתואמת את הקטגוריה , והאקטיביטי חייבת להיות בעלת אקשיין סנד אינטנט פילטר(מה שכבר קיים אם האקטיביטי שלכם כבר בשייר שיט)
  90. בשביל ליצור שיירינגשורטקאט צריך לבנות קישור דינאמי כרגיל, עם תוספת של שדה קטגוריה (חובה), וכדאי גם להוסיף שדה של פרסן. שיירינג שורטקאט חייב להיות מקושר עם אקטיביטי שתואמת את הקטגוריה , והאקטיביטי חייבת להיות בעלת אקשיין סנד אינטנט פילטר(מה שכבר קיים אם האקטיביטי שלכם כבר בשייר שיט)
  91. בשביל ליצור שיירינגשורטקאט צריך לבנות קישור דינאמי כרגיל, עם תוספת של שדה קטגוריה (חובה), וכדאי גם להוסיף שדה של פרסן. שיירינג שורטקאט חייב להיות מקושר עם אקטיביטי שתואמת את הקטגוריה , והאקטיביטי חייבת להיות בעלת אקשיין סנד אינטנט פילטר(מה שכבר קיים אם האקטיביטי שלכם כבר בשייר שיט)
  92. בשביל ליצור שיירינגשורטקאט צריך לבנות קישור דינאמי כרגיל, עם תוספת של שדה קטגוריה (חובה), וכדאי גם להוסיף שדה של פרסן. שיירינג שורטקאט חייב להיות מקושר עם אקטיביטי שתואמת את הקטגוריה , והאקטיביטי חייבת להיות בעלת אקשיין סנד אינטנט פילטר(מה שכבר קיים אם האקטיביטי שלכם כבר בשייר שיט)
  93. בשביל ליצור שיירינגשורטקאט צריך לבנות קישור דינאמי כרגיל, עם תוספת של שדה קטגוריה (חובה), וכדאי גם להוסיף שדה של פרסן. שיירינג שורטקאט חייב להיות מקושר עם אקטיביטי שתואמת את הקטגוריה , והאקטיביטי חייבת להיות בעלת אקשיין סנד אינטנט פילטר(מה שכבר קיים אם האקטיביטי שלכם כבר בשייר שיט)
  94. בשביל ליצור שיירינגשורטקאט צריך לבנות קישור דינאמי כרגיל, עם תוספת של שדה קטגוריה (חובה), וכדאי גם להוסיף שדה של פרסן. שיירינג שורטקאט חייב להיות מקושר עם אקטיביטי שתואמת את הקטגוריה , והאקטיביטי חייבת להיות בעלת אקשיין סנד אינטנט פילטר(מה שכבר קיים אם האקטיביטי שלכם כבר בשייר שיט)
  95. ואיך מוסיפים מידע על התוכן שאנחנו רוצים לשתף
  96. בשביל להוסיף תוכן משמעותי לשיירינג שיט צריך לעשות שיירינג אינטנט כרגיל. ואז מוסיפים את המטאדאטה לפני הקריאה לקרייטצ’וזר. שימו לב לתת את ההרשאות המתאימות בשביל שהמערכת תקרא את הט’מבנייל, אחרת התמונה לא תופיעה
  97. בשביל להוסיף תוכן משמעותי לשיירינג שיט צריך לעשות שיירינג אינטנט כרגיל. ואז מוסיפים את המטאדאטה לפני הקריאה לקרייטצ’וזר. שימו לב לתת את ההרשאות המתאימות בשביל שהמערכת תקרא את הט’מבנייל, אחרת התמונה לא תופיעה
  98. בשביל להוסיף תוכן משמעותי לשיירינג שיט צריך לעשות שיירינג אינטנט כרגיל. ואז מוסיפים את המטאדאטה לפני הקריאה לקרייטצ’וזר. שימו לב לתת את ההרשאות המתאימות בשביל שהמערכת תקרא את הט’מבנייל, אחרת התמונה לא תופיעה
  99. בשביל להוסיף תוכן משמעותי לשיירינג שיט צריך לעשות שיירינג אינטנט כרגיל. ואז מוסיפים את המטאדאטה לפני הקריאה לקרייטצ’וזר. שימו לב לתת את ההרשאות המתאימות בשביל שהמערכת תקרא את הט’מבנייל, אחרת התמונה לא תופיעה