Slide 1

Slide 1 text

New Result Activity / Fragment pluu

Slide 2

Slide 2 text

Deprecated.

Slide 3

Slide 3 text

Index 1. New Activity Result 2. New Fragment Result

Slide 4

Slide 4 text

New Activity Result.

Slide 5

Slide 5 text

Release Notes Add • activity:1.2.0-alpha02 • fragment:1.3.0-alpha02 API Deprecated • activity:1.2.0-alpha04 • fragment:1.3.0-alpha04

Slide 6

Slide 6 text

Deprecated Deprecated Old Request class SampleActivity : AppCompatActivity() { private val request_second_code = 100 private fun sta rt SecondView() { val intent = Intent(this, ResultSecondActivity::class.java) sta rt ActivityForResult(intent, request_second_code) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == request_second_code) { // do action } } } Basic

Slide 7

Slide 7 text

Old Request class SampleActivity : AppCompatActivity() { private val request_location_code = 101 private fun requestLocation() { val permission = Manifest.permission.ACCESS_FINE_LOCATION if (checkPermissions(permission)) { toast("Permission granted") } else { requestPermissions(arrayOf(permission), request_location_code) } } private fun checkPermissions(vararg permissions: String): Boolean { return permissions.all { permission -> ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED } } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == request_location_code) { if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) { toast("Permission granted") } else { toast("Permission denied or canceled") } } } Location

Slide 8

Slide 8 text

Deprecation ComponentActivity FragmentActivity Fragment sta rt ActivityForResult() @Deprecated @SuppressWarnings 
 (“deprecation”) @Deprecated onActivityResult() @Deprecated @SuppressWarnings 
 (“deprecation”) @Deprecated requestPermissions() - - @Deprecated onRequestPermissionsResult() @Deprecated @SuppressWarnings 
 (“deprecation”) @Deprecated

Slide 9

Slide 9 text

New Result API class SampleActivity : AppCompatActivity() { private val requestActivity = registerForActivityResult( Sta rt ActivityForResult() ) { activityResult -> // do action } private fun sta rt SecondView() { val intent = Intent(context, ResultSecondActivity::class.java) requestActivity.launch(intent) } } Basic

Slide 10

Slide 10 text

New Result API class SampleActivity : AppCompatActivity() { private val requestLocation = registerForActivityResult( RequestPermission(), ACCESS_FINE_LOCATION ) { isGranted -> // do action } private fun sta rt Location() { requestLocation.launch() } } Location [ ✔︎ ] checkSelfPermission [ ✔︎ ] requestPermissions [ ✔︎ ] call ActivityResultCallback

Slide 11

Slide 11 text

Structure class ActivityResultSampleActivity : AppCompatActivity() { private val requestActivity = registerForActivityResult( Sta rt ActivityForResult() ) { activityResult -> // do action } private fun sta rt SecondView() { requestActivity.launch(/**create intent*/) } }

Slide 12

Slide 12 text

Signature h tt ps://cs.android.com/androidx/pla tf orm/frameworks/suppo rt /+/androidx-main:activity/activity/src/main/java/androidx/activity/ComponentActivity.java @NonNull @Override public fi nal ActivityResultLauncher registerForActivityResult( @NonNull ActivityResultContract contract, @NonNull ActivityResultCallback callback) { return registerForActivityResult(contract, mActivityResultRegistry, callback); } @NonNull @Override public fi nal ActivityResultLauncher registerForActivityResult( @NonNull fi nal ActivityResultContract contract, @NonNull fi nal ActivityResultRegistry registry, @NonNull fi nal ActivityResultCallback callback) { return registry.register( "activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback); } class ActivityResultSampleActivity : AppCompatActivity() { private val requestActivity = registerForActivityResult( Sta rt ActivityForResult() ) { activityResult -> // do action } }

Slide 13

Slide 13 text

ActivityResultRegistry ActivityResultCallbackਸ ੷੢ೞח Registry ActivityResult* ActivityResultContract Input Typeী ٮܲ Activityܳ ഐ୹ೞҊ Output Typeਵ۽ ୊ܻೞب۾ ҅ড ActivityResultCallback Activity#onActivityResult੉ ഐ୹ؼ ٸ ࢎਊغח Callback ActivityResultLauncher ActivityResultContractܳ प೯ೞӝ ਤೠ Launcher FragmentActivity ComponentActivity ActivityResultCaller ActivityResult झఋੌ ഐ୹੗ Fragment

Slide 14

Slide 14 text

Signature ActivityResultContract Input createIntent Output Sta rt ActivityForResult Intent ActivityResult GetContent String Intent.ACTION_GET_CONTENT Uri RequestMultiplePermissions String[] Map PickContact Void Intent.ACTION_PICK Uri TakeVideo Uri MediaStore.ACTION_VIDEO_CAPTURE Bitmap OpenDocument String[] Intent.ACTION_OPEN_DOCUMENT Uri TakePicture Uri MediaStore.ACTION_IMAGE_CAPTURE Boolean Sta rt IntentSenderForResult IntentSenderRequest ActivityResult OpenDocumentTree Uri Intent.ACTION_OPEN_DOCUMENT_TREE Uri OpenMultipleDocuments String[] Intent.ACTION_OPEN_DOCUMENT List TakePicturePreview Void MediaStore.ACTION_IMAGE_CAPTURE Bitmap CreateDocument String Intent.ACTION_CREATE_DOCUMENT Uri GetMultipleContents String Intent.ACTION_GET_CONTENT List RequestPermission String Boolean

Slide 15

Slide 15 text

StartActivityForResult public static fi nal class Sta rt ActivityForResult extends ActivityResultContract { public static fi nal String EXTRA_ACTIVITY_OPTIONS_BUNDLE = "androidx.activity.result" + ".contract.extra.ACTIVITY_OPTIONS_BUNDLE"; @NonNull @Override public Intent createIntent(@NonNull Context context, @NonNull Intent input) { return input; } @NonNull @Override public ActivityResult parseResult( int resultCode, @Nullable Intent intent) { return new ActivityResult(resultCode, intent); } } onActivityResult

Slide 16

Slide 16 text

RequestPermission public static fi nal class RequestPermission extends ActivityResultContract { @NonNull @Override public Intent createIntent(@NonNull Context context, @NonNull String input) { return RequestMultiplePermissions.createIntent(new String[] { input }); } @NonNull @Override public Boolean parseResult(int resultCode, @Nullable Intent intent) { if (intent == null || resultCode != Activity.RESULT_OK) return false; int[] grantResults = intent.getIntArrayExtra(EXTRA_PERMISSION_GRANT_RESULTS); if (grantResults == null || grantResults.length == 0) return false; return grantResults[0] == PackageManager.PERMISSION_GRANTED; } @Override public @Nullable SynchronousResult getSynchronousResult( @NonNull Context context, @Nullable String input) { if (input == null) { return new SynchronousResult<>(false); } else if (ContextCompat.checkSelfPermission(context, input) == PackageManager.PERMISSION_GRANTED) { return new SynchronousResult<>(true); } else { // proceed with permission request return null; } } } onRequestPermissionsResult launch

Slide 17

Slide 17 text

RequestCodeо হחؘਃ?!

Slide 18

Slide 18 text

requestCode public abstract class ActivityResultRegistry { @NonNull public fi nal ActivityResultLauncher register( @NonNull fi nal String key, @NonNull fi nal LifecycleOwner lifecycleOwner, @NonNull fi nal ActivityResultContract contract, @NonNull fi nal ActivityResultCallback callback) { ... fi nal int requestCode = registerKey(key); ... return new ActivityResultLauncher() { @Override public void launch(I input, @Nullable ActivityOptionsCompat options) { onLaunch(requestCode, contract, input, options); } ... }; } } h tt ps://cs.android.com/androidx/pla tf orm/frameworks/suppo rt /+/androidx-main:activity/activity/src/main/java/androidx/activity/result/ActivityResultRegistry.java generated requestCode

Slide 19

Slide 19 text

requestCode public abstract class ActivityResultRegistry { // Use upper 16 bits for request codes private static fi nal int INITIAL_REQUEST_CODE_VALUE = 0x00010000; private Random mRandom = new Random(); /** * Generate a random number between the initial value (00010000) inclusive, and the max * integer value. If that number is already an existing request code, generate another until * we fi nd one that is new. * * @return the number */ private int generateRandomNumber() { int number = mRandom.nextInt((Integer.MAX_VALUE - INITIAL_REQUEST_CODE_VALUE) + 1) + INITIAL_REQUEST_CODE_VALUE; while (mRcToKey.containsKey(number)) { number = mRandom.nextInt((Integer.MAX_VALUE - INITIAL_REQUEST_CODE_VALUE) + 1) + INITIAL_REQUEST_CODE_VALUE; } return number; } } Range 65536 ~ Int.MAX_VALUE h tt ps://cs.android.com/androidx/pla tf orm/frameworks/suppo rt /+/androidx-main:activity/activity/src/main/java/androidx/activity/result/ActivityResultRegistry.java

Slide 20

Slide 20 text

ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry() { @Override public void onLaunch( fi nal int requestCode, @NonNull ActivityResultContract contract, I input, @Nullable ActivityOptionsCompat options) { ComponentActivity activity = ComponentActivity.this; // Immediate result path fi nal ActivityResultContract.SynchronousResult synchronousResult = contract.getSynchronousResult(activity, input); if (synchronousResult != null) { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { dispatchResult(requestCode, synchronousResult.getValue()); } }); return; } // Sta rt activity path ... } }; ActivityResultRegistry (1)

Slide 21

Slide 21 text

@Override public void onLaunch(...) { // Sta rt activity path Intent intent = contract.createIntent(activity, input); ... if (ACTION_REQUEST_PERMISSIONS.equals(intent.getAction())) { // requestPermissions path String[] permissions = intent.getStringArrayExtra(EXTRA_PERMISSIONS); if (permissions == null) { return; } List nonGrantedPermissions = new ArrayList<>(); for (String permission : permissions) { if (checkPermission(permission, android.os.Process.myPid(), android.os.Process.myUid()) != PackageManager.PERMISSION_GRANTED) { nonGrantedPermissions.add(permission); } } if (!nonGrantedPermissions.isEmpty()) { ActivityCompat.requestPermissions(activity, nonGrantedPermissions.toArray(new String[0]), requestCode); } } else if (ACTION_INTENT_SENDER_REQUEST.equals(intent.getAction())) { ... } else { // sta rt ActivityForResult path ActivityCompat.sta rt ActivityForResult(activity, intent, requestCode, optionsBundle); } } ActivityResultRegistry (2) Permissions sta rt ActivityForResult

Slide 22

Slide 22 text

Activity / Fragment ActivityResultContract parseResult createIntent ActivityResultLauncher launch ActivityResultCaller dispatchResult ActivityResultRegistry onLaunch register ActivityResultCallback onActivityResult LifecycleEventObserver ON_START onRequestPermissionsResult onActivityResult requestPermissions sta rt ActivityForResult registerForActivityResult

Slide 23

Slide 23 text

New Fragment Result.

Slide 24

Slide 24 text

New Fragment Result API • Update, fragment:1.3.0-alpha04 • Fragmentח Ѿҗ ࣻन റ, ࢤݺ઱ӝо STARTED ੌ ٸ ௒ߔ ܻझց प೯ • Fragment Ѿҗо FragmentManagerী ੷੢ • ParentFragmentManagerܳ ࢎਊೞৈ setFragmentResultListener() ژח setFragmentResult() API ࢎਊ

Slide 25

Slide 25 text

Sample Master/Detail Master Fragment Detail Fragment

Slide 26

Slide 26 text

Old Version class FragmentB : Fragment() { // Fragment р੄ ాनਊ Listener ੿੄ inte rf ace OnResultListener { fun onResult(value: String) } private var listener: OnResultListener? = null fun setListener(listener: OnResultListener) { this.listener = listener } private fun clickDone() { listener?.onResult(/** Write result */) } } class FragmentA : Fragment(), FragmentB.OnResultListener { private fun showFragmentB() { parentFragmentManager.commit { replace(R.id.container, FragmentB().apply { // FragmentB ಴दೡٸ Listenerܳ ੹׳ setListener(this@FragmentA) }) addToBackStack(null) } } // Implement FragmentB.OnResultListener override fun onResult(value: String) { // do action } } (1) setListener

Slide 27

Slide 27 text

Old Version class FragmentB : Fragment() { // Fragment р੄ ాनਊ Listener ੿੄ inte rf ace OnResultListener { fun onResult(value: String) } private var listener: OnResultListener? = null override fun onA tt ach(context: Context) { super.onA tt ach(context) val p = parentFragment if (p is OnResultListener) { this.listener = p } } private fun clickDone() { listener?.onResult(/** Write result */) } } class FragmentA : Fragment(), FragmentB.OnResultListener { private fun showFragmentB() { parentFragmentManager.commit { replace(R.id.container, FragmentB()) addToBackStack(null) } } // Implement FragmentB.OnResultListener override fun onResult(value: String) { // do action } } (2) Check ParentFragment

Slide 28

Slide 28 text

Use SharedViewModel class ListFragment : Fragment() { // Using the activityViewModels() Kotlin prope rt y delegate from the // fragment-ktx a rt ifact to retrieve the ViewModel in the activity scope private val viewModel: ItemViewModel by activityViewModels() // Called when the item is clicked fun onItemClicked(item: Item) { // Set a new item viewModel.selectItem(item) } } class MainActivity : AppCompatActivity() { // Using the viewModels() Kotlin prope rt y delegate from the activity-ktx // a rt ifact to retrieve the ViewModel in the activity scope private val viewModel: ItemViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.selectedItem.observe(this, Observer { item -> // Pe rf orm an action with the latest item data }) } } h tt ps://developer.android.com/guide/fragments/communicate#fragments

Slide 29

Slide 29 text

New Fragment Result class DetailFragment : Fragment(R.layout.fragment_detail) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setFragmentResultListener(MasterFragment.requestKey) { _, bundle -> binding.tvLabel.text = bundle.getString( MasterFragment.resultKey ) } } } class MasterFragment : ListFragment() { private val list = (0..20).map { "Item $it" } override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) { super.onListItemClick(l, v, position, id) setFragmentResult( requestKey, bundleOf(resultKey to list[position]) ) } companion object { const val requestKey = " fl exible" const val resultKey = "item" } }

Slide 30

Slide 30 text

Via FragmentManager h tt ps://developer.android.com/guide/fragments/communicate

Slide 31

Slide 31 text

FragmentManager public abstract class FragmentManager implements FragmentResultOwner { private fi nal ConcurrentHashMap mResults = new ConcurrentHashMap<>(); private fi nal ConcurrentHashMap mResultListeners = new ConcurrentHashMap<>(); } private static class LifecycleAwareResultListener implements FragmentResultListener { private fi nal Lifecycle mLifecycle; private fi nal FragmentResultListener mListener; private fi nal LifecycleEventObserver mObserver; LifecycleAwareResultListener(@NonNull Lifecycle lifecycle, @NonNull FragmentResultListener listener, @NonNull LifecycleEventObserver observer) { mLifecycle = lifecycle; mListener = listener; mObserver = observer; } public boolean isAtLeast(Lifecycle.State state) { return mLifecycle.getCurrentState().isAtLeast(state); } @Override public void onFragmentResult(@NonNull String requestKey, @NonNull Bundle result) { mListener.onFragmentResult(requestKey, result); } public void removeObserver() { mLifecycle.removeObserver(mObserver); } }

Slide 32

Slide 32 text

FragmentManager @Override public fi nal void setResult(@NonNull String requestKey, @Nullable Bundle result) { if (result == null) { // if the given result is null, remove the result mResults.remove(requestKey); return; } // Check if there is a listener waiting for a result with this key LifecycleAwareResultListener resultListener = mResultListeners.get(requestKey); // if there is and it is sta rt ed, fi re the callback if (resultListener != null && resultListener.isAtLeast(Lifecycle.State.STARTED)) { resultListener.onFragmentResult(requestKey, result); } else { // else, save the result for later mResults.put(requestKey, result); } } @SuppressLint("SyntheticAccessor") @Override public fi nal void setResultListener(@NonNull fi nal String requestKey, @NonNull fi nal LifecycleOwner lifecycleOwner, @Nullable fi nal FragmentResultListener listener) { if (listener == null) { mResultListeners.remove(requestKey); return; } fi nal Lifecycle lifecycle = lifecycleOwner.getLifecycle(); if (lifecycle.getCurrentState() == Lifecycle.State.DESTROYED) { return; } LifecycleEventObserver observer = new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_START) { Bundle storedResult = mResults.get(requestKey); if (storedResult != null) { listener.onFragmentResult(requestKey, storedResult); setResult(requestKey, null); } } if (event == Lifecycle.Event.ON_DESTROY) { lifecycle.removeObserver(this); mResultListeners.remove(requestKey); } } }; lifecycle.addObserver(observer); mResultListeners.put(requestKey, new LifecycleAwareResultListener(lifecycle, listener)); }

Slide 33

Slide 33 text

Sample 2 Fragment (Stack 3) in Fragment (Stack 2) in Fragment (Stack 1) in Activity Activity Stack 1 Stack 2 Stack 3 setResult Stack

Slide 34

Slide 34 text

Stack Style Parent੄ ChildFragment == Child੄ ParentFragment

Slide 35

Slide 35 text

Stack Style Activity Stack 1 Fragment Stack 2 Fragment Parent FragmentManager Child FragmentManager Parent FragmentManager Child FragmentManager Suppo rt FragmentManager Fragment Manager

Slide 36

Slide 36 text

Stack Style Activity Stack 1 Fragment Stack 2 Fragment Suppo rt FragmentManager Fragment Manager Parent FragmentManager Child FragmentManager Parent FragmentManager Child FragmentManager (3) setFragmentResult (1) setFragmentResultListener (2) setFragmentResultListener (4) setFragmentResult

Slide 37

Slide 37 text

Reference • Ge tt ing a result from an activity : h tt ps://developer.android.com/training/ basics/intents/result • Communicating with fragments : h tt ps://developer.android.com/guide/ fragments/communicate

Slide 38

Slide 38 text

END.