Share
Embed
What's New in Android - I/O Extended Yangon 2018
Talk from I/O Extended Yangon 2018.
Aung Kyaw Paing
July 08, 2018
Transcript
94 slides
-
Notch Support - DisplayCount class - getDisplayCutout() method - New
window attribute, layoutInDisplayCutoutMode - LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT - LAYOUT_IN_DISPLAY_CUTOUT_SHORT EDGES - LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER -
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT - The notch has to be contained in System
Bar - If not, the window won’t overlap it -
Notch Support if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { val windowAttributes =
window.attributes windowAttributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT } -
Notification - Person API - Can now display Image -
Reply drafts - Group conversations support - Semantic Action (Reply, Mark as read, thumbs up… etc.) - Smart Reply Photo Ⓒ Google -
Notification // create new Person val sender = Person() .setName(name)
.setUri(uri) .setIcon(null) .build() // create image message val message = Message("Picture", time, sender) .setData("image/";, imageUri) val style = Notification.MessagingStyle(getUser()) .addMessage("Check this out!", 0, sender) .addMessage(message) -
Notification Channel - Can block entire “groups” of channel -
Can check if blocked via isBlocked - New policies -
TextView Photo Ⓒ Best practices for text on Android (
Google IO / 18 ) -
PreComputed Text // UI thread val params: PrecomputedText.Params = textView.getTextMetricsParams()
val ref = WeakReference(textView) executor.execute { // background thread val text = PrecomputedText.create("Hello", params) val textView = ref.get() textView?.post { // UI thread val textViewRef = ref.get() textViewRef?.text = text } } -
Magnifier fun onTouchEvent(event: MotionEvent): Boolean { when (event.actionMasked) { MotionEvent.ACTION_DOWN
-> magnifier.show(event.x, event.y) MotionEvent.ACTION_MOVE -> magnifier.show(event.x, event.y) MotionEvent.ACTION_UP -> magnifier.dismiss() } } -
Smart Linkify // UI thread val text: Spannable = ...
val request = TextLinks.Request.Builder(text) val ref = WeakReference(textView) executor.execute { // background thread TextClassifier.generateLinks(request).apply(text) val textView = ref.get() textView?.post { // UI thread val textViewRef = ref.get() textViewRef?.text = text } } -
And…. - Indoor positioning - Multi-camera support - ImageDecoder -
AnimatedImageDrawable - Client side encryptions for backups - New accessibility features - Autofill improvements - And Many more! -
Android KTX sharedPreferences.edit { putBoolean("key", value) putLong("key2", value2) } val
args = bundleOf { "key" to 1, "key2" to true } -
Slices - UI templates with interactive contents - Used in
Google Search, Google Assistant -
Slices fun createSliceWithGridRow(sliceUri: Uri): Slice { // Create the parent
builder. return list(context, sliceUri, ListBuilder.INFINITY) { header { setTitle("Famous restaurants") setPrimaryAction(SliceAction(pendingIntent, icon, "Famous restaurants")) } gridRow { cell { addImage(image1, LARGE_IMAGE) addTitleText("Top Restaurant") addText("0.3 mil") setContentIntent(intent1) } cell { addImage(image2, LARGE_IMAGE) addTitleText("Fast and Casual") addText("0.5 mil") setContentIntent(intent2) } } } } -
RecyclerView ListAdapter companion object { private val MOVIE_DIFF_CALLBACK = object
: DiffUtil.ItemCallback<Movie>() { override fun areItemsTheSame(item1: Movie, item2: Movie): Boolean { return item1.id == item2.id } override fun areContentsTheSame(item1: Movie, item2: Movie): Boolean { return item1 == item2 } } } -
class MovieRecyclerViewAdapter : ListAdapter<Movie, MovieViewHolder>(MOVIE_DIFF_CALLBACK) { companion object { private
val MOVIE_DIFF_CALLBACK = object : DiffUtil.ItemCallback<Movie>() { override fun areItemsTheSame(item1: Movie, item2: Movie): Boolean { return item1.id == item2.id } override fun areContentsTheSame(item1: Movie, item2: Movie): Boolean { return item1 == item2 } } } } RecyclerView ListAdapter -
RecyclerView Selection - Allow select/deselect multiple items in list SelectionTracker
tracker = new SelectionTracker.Builder<>( "my-selection-id", recyclerView, new StableIdKeyProvider(recyclerView), new MyDetailsLookup(recyclerView), StorageStrategy.createLongStorage()) .build(); -
BottomAppBar <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- Other components and
views --> <com.google.android.material.bottomappbar.BottomAppBar android:id="@+id/bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" app:navigationIcon="@drawable/ic_menu_24"/> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_anchor="@id/bar"/> </android.support.design.widget.CoordinatorLayout> -
MaterialCardView <com.google.android.material.card.MaterialCardView style="@style/Widget.MaterialComponents.CardView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/mtrl_card_spacing" android:layout_marginTop="@dimen/mtrl_card_spacing" android:layout_marginRight="@dimen/mtrl_card_spacing" android:minHeight="200dp"> <!--
Card contents. --> </com.google.android.material.card.MaterialCardView> -
Chips <com.google.android.material.chip.ChipGroup android:layout_width="match_parent" android:layout_height="wrap_content"> <!-- Chips can be declared here,
or added dynamically. --> </com.google.android.material.chip.ChipGroup> -
- Use for enqueuing background works - JobScheduler for API
23+ - Firebase JobDispatcher (or) custom AlarmManager + Broadcast Receiver for backport WorkManager -
val workManager = WorkManager.getInstance() val workConstraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.METERED) .setRequiresCharging(true)
.setRequiresStorageNotLow(false) .build() val workRequest = OneTimeWorkRequest.Builder(SyncWorker::class.java) .setConstraints(workConstraints) .build() workManager.enqueue(workRequest) WorkManager -
val workManager = WorkManager.getInstance() val workRequest = OneTimeWorkRequest.Builder(SyncWorker::class.java).build() workManager.enqueue(workRequest) val
workStatusLiveData = workManager.getStatusById(workRequest.id) workStatusLiveData.observe(this, Observer { workStatus -> when (workStatus?.state) { State.ENQUEUED -> TODO() State.RUNNING -> TODO() State.SUCCEEDED -> TODO() State.FAILED -> TODO() State.BLOCKED -> TODO() State.CANCELLED -> TODO() } }) WorkManager -
WorkManager val workManager = WorkManager.getInstance() val request1 = OneTimeWorkRequest.Builder(FooWorker::class.java).build() val
request2 = OneTimeWorkRequest.Builder(BarWorker::class.java).build() val request3 = OneTimeWorkRequest.Builder(BazWorker::class.java).build() workManager.beginWith(request1, request2).then(request3).enqueue() -
Navigation Arch Component - Single Activity, Multiple Fragment - Uses
“Stack”, last-in first out structure - Deep Link has same stack -
Navigation Arch Component <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android"
app:startDestination=“@id/blankFragment"> <fragment android:id="@+id/blankFragment" android:name="com.example.cashdog.cashdog.BlankFragment" android:label="fragment_blank" tools:layout="@layout/fragment_blank" /> <fragment android:id="@+id/blankFragment2" android:name="com.example.cashdog.cashdog.BlankFragment2" android:label="Blank2" tools:layout="@layout/fragment_blank_fragment2" /> </navigation> -
Navigation Arch Component <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android"
app:startDestination=“@id/blankFragment"> <fragment android:id="@+id/blankFragment" android:name="com.example.cashdog.cashdog.BlankFragment" android:label="fragment_blank" tools:layout="@layout/fragment_blank" > <action android:id="@+id/action_blankFragment_to_blankFragment2" app:destination="@id/blankFragment2" /> </fragment> <fragment android:id="@+id/blankFragment2" android:name="com.example.cashdog.cashdog.BlankFragment2" android:label="fragment_blank_fragment2" tools:layout="@layout/fragment_blank_fragment2" /> </navigation> -
Navigation Arch Component ?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/my_nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/nav_graph" app:defaultNavHost="true" /> </android.support.constraint.ConstraintLayout> -
<fragment android:id="@+id/details_page_fragment" android:label="@string/details" android:name="com.example.android.myapp.DetailsFragment" /> <item android:id="@id/details_page_fragment" android:icon="@drawable/ic_details" android:title="@string/details" />
Navigation Arch Component - NavigationView(Drawer) -
<fragment android:id="@+id/details_page_fragment" android:label="@string/details" android:name="com.example.android.myapp.DetailsFragment" /> <item android:id="@id/details_page_fragment" android:icon="@drawable/ic_details" android:title="@string/details" />
val navController = Navigation.findNavController(this, R.id.nav_host_fragment) val navigationView = findViewById<NavigationView>(R.id.nav_view) navigationView.setupWithNavController(navController) Navigation Arch Component - NavigationView(Drawer) -
Navigation Arch Component - Arguments <fragment android:id="@+id/confirmationFragment" android:name="com.example.cashdog.cashdog.ConfirmationFragment" android:label="fragment_confirmation" tools:layout="@layout/fragment_confirmation">
<argument android:name="amount" android:defaultValue=”0” /> -
Navigation Arch Component - Arguments <fragment android:id="@+id/confirmationFragment" android:name="com.example.cashdog.cashdog.ConfirmationFragment" android:label="fragment_confirmation" tools:layout="@layout/fragment_confirmation">
<argument android:name="amount" android:defaultValue=”0” /> var bundle = bundleOf("amount" to amount) view.findNavController().navigate(R.id.confirmationAction, bundle) -
Navigation Arch Component - Arguments <fragment android:id="@+id/confirmationFragment" android:name="com.example.cashdog.cashdog.ConfirmationFragment" android:label="fragment_confirmation" tools:layout="@layout/fragment_confirmation">
<argument android:name="amount" android:defaultValue=”0” /> var bundle = bundleOf("amount" to amount) view.findNavController().navigate(R.id.confirmationAction, bundle) val tv = view.findViewById(R.id.textViewAmount) tv.text = arguments.getString("amount") -
Navigation Arch Component - Safe Args apply plugin: 'com.android.application' apply
plugin: 'androidx.navigation.safeargs' android { //... } -
Navigation Arch Component - Safe Args <fragment android:id="@+id/confirmationFragment" android:name="com.example.buybuddy.buybuddy.ConfirmationFragment" android:label="fragment_confirmation"
tools:layout="@layout/fragment_confirmation"> <argument android:name="amount" android:defaultValue="1" app:type="integer"/> </fragment> -
Navigation Arch Component - Safe Args <fragment android:id="@+id/specifyAmountFragment" android:name="com.example.buybuddy.buybuddy.SpecifyAmountFragment" android:label="fragment_specify_amount"
tools:layout="@layout/fragment_blank" > <action android:id="@+id/action_specify_to_confirm" app:destination="@id/confirmationFragment" /> </fragment> <fragment android:id="@+id/confirmationFragment" android:name="com.example.buybuddy.buybuddy.ConfirmationFragment" android:label="fragment_confirmation" tools:layout="@layout/fragment_confirmation"> <argument android:name="amount" android:defaultValue="1" app:type="integer"/ > </fragment> -
Navigation Arch Component - Safe Args override fun onClick(v: View?)
{ val amount = 100 val action = SpecifyAmountFragmentDirections.confirmationAction(amount) v.findNavController().navigate(action) } -
Navigation Arch Component - Safe Args override fun onClick(v: View?)
{ val amount = 100 val action = SpecifyAmountFragmentDirections.confirmationAction(amount) v.findNavController().navigate(action) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tv: TextView = view.findViewById(R.id.textViewAmount) val amount = ConfirmationFragmentArgs.fromBundle(arguments).amount tv.text = amount.toString() } -
ConstraintLayout 1.1 - Barrier - Group - Circular constraints -
Chains - Constraint set -
- Kotlin Support - New APIs assertEquals(view.visibility, View.VISIBLE) “Failed Expected
0 but was 16” Testing -
- Kotlin Support - New APIs assertThat(view).isVisible() “Failed, view was
not visible” Testing -
Testing - Roboelectric to be packaged inside androidx.test - Roboelectric
semantics will be same as Espresso http://robolectric.org/blog/2018/05/09/robolectric-4-0-alpha/ -
Testing - Roboelectric to be packaged inside androidx.test - Roboelectric
semantics will be same as Espresso - Will be able to run UI test in local JVM http://robolectric.org/blog/2018/05/09/robolectric-4-0-alpha/ -
App Bundle - A new publishing format, instead of APK
- Minimized APK depends on user’s device - Deliver downloadable “dynamic feature” -
D8 - New dex-ing tool - Default already on Android
Studio 3.1 - Manually turn on by adding android.enableD8=true in gradle.properties -
R8 - Better Proguard - Can include proguard rules in
java library module by placing inside “META-INF/proguard” -
Emulator - Saving states (Cold boot / Hot boot) -
AMD CPU support - Notch emulation - Sceneform emulation -
Android Studio Canary - Navigation Arch Component Support - MotionLayout
Support - Better Profiler - WebP support -
Play Store - Digital Rights Management(DRM) metadata - App now
have to be complaint with GDPR - Algorithm changes - Must have minimum API level 26 by end of August 2018