Skip to content

Commit

Permalink
feature: add GroupSceneCompatUtility, provides the ability to bind Gr…
Browse files Browse the repository at this point in the history
…oupScene to Fragment directly
  • Loading branch information
qii committed Nov 22, 2019
1 parent 043fab3 commit 3be4397
Show file tree
Hide file tree
Showing 10 changed files with 272 additions and 12 deletions.
5 changes: 3 additions & 2 deletions demo/src/main/AndroidManifest.xml
Expand Up @@ -22,13 +22,14 @@
</intent-filter>
</activity>
<activity android:name=".benchmark.performance.EmptyAppCompatActivity" />
<activity android:name=".migrate.FragmentBindingDemoActivity" />
<activity android:name=".migrate.NavigationSceneBindToFragmentSample" />
<activity android:name=".migrate.TestSceneToViewActivity" />
<activity android:name="com.bytedance.scenedemo.navigation.forresult.TestActivityResultActivity" />
<activity android:name="com.bytedance.scenedemo.activity_compatibility.scene_result.ActivityGetSceneResultSampleActivity" />
<activity android:name=".restore.SupportRestoreActivity" />
<activity android:name=".migrate.migrate_from_classic_activity_fragment.MigrateFromClassicAndroidActivitySamplesActivity" />
<activity android:name=".migrate.GroupSceneBindingSample" />
<activity android:name=".migrate.GroupSceneBindToActivitySample" />
<activity android:name=".migrate.GroupSceneBindToFragmentSample"/>
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Expand Down
13 changes: 9 additions & 4 deletions demo/src/main/java/com/bytedance/scenedemo/MigrateSamples.kt
Expand Up @@ -8,8 +8,9 @@ import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.ScrollView
import com.bytedance.scene.Scene
import com.bytedance.scenedemo.migrate.FragmentBindingDemoActivity
import com.bytedance.scenedemo.migrate.GroupSceneBindingSample
import com.bytedance.scenedemo.migrate.NavigationSceneBindToFragmentSample
import com.bytedance.scenedemo.migrate.GroupSceneBindToFragmentSample
import com.bytedance.scenedemo.migrate.GroupSceneBindToActivitySample
import com.bytedance.scenedemo.migrate.TestSceneToViewActivity
import com.bytedance.scenedemo.migrate.migrate_from_classic_activity_fragment.MigrateFromClassicAndroidActivitySamplesActivity
import com.bytedance.scenedemo.utility.addButton
Expand All @@ -32,11 +33,15 @@ class MigrateSamples : Scene() {
addTitle(layout, getString(R.string.main_title_basic))

addButton(layout, getString(R.string.main_part_btn_bind_navigationscene_to_fragment), View.OnClickListener {
requireNavigationScene().startActivity(Intent(activity, FragmentBindingDemoActivity::class.java))
requireNavigationScene().startActivity(Intent(activity, NavigationSceneBindToFragmentSample::class.java))
})

addButton(layout, getString(R.string.main_part_btn_bind_groupscene_to_activity), View.OnClickListener {
requireNavigationScene().startActivity(Intent(activity, GroupSceneBindingSample::class.java))
requireNavigationScene().startActivity(Intent(activity, GroupSceneBindToActivitySample::class.java))
})

addButton(layout, getString(R.string.main_part_btn_bind_groupscene_to_fragment), View.OnClickListener {
requireNavigationScene().startActivity(Intent(activity, GroupSceneBindToFragmentSample::class.java))
})

addButton(layout, getString(R.string.main_part_btn_bind_to_view), View.OnClickListener {
Expand Down
Expand Up @@ -5,7 +5,7 @@ import android.os.Bundle
import com.bytedance.scene.GroupSceneUtility
import com.bytedance.scenedemo.group.viewpager.ViewPagerSample

class GroupSceneBindingSample : Activity() {
class GroupSceneBindToActivitySample : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GroupSceneUtility.setupWithActivity(this, ViewPagerSample::class.java).build()
Expand Down
@@ -0,0 +1,39 @@
package com.bytedance.scenedemo.migrate

import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import com.bytedance.scene.GroupSceneDelegate
import com.bytedance.scene.ui.GroupSceneCompatUtility
import com.bytedance.scene.utlity.ViewIdGenerator
import com.bytedance.scenedemo.group.viewpager.ViewPagerSample

/**
* Created by JiangQi on 9/5/18.
*/
class GroupSceneBindToFragmentSample : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportFragmentManager.beginTransaction().add(android.R.id.content, AFragment()).commitNow()
}

class AFragment : Fragment() {
private val viewId = ViewIdGenerator.generateViewId()
private var delegate: GroupSceneDelegate? = null

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val frameLayout = FrameLayout(activity!!)
frameLayout.id = viewId
return frameLayout
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
delegate = GroupSceneCompatUtility.setupWithFragment(this, ViewPagerSample::class.java, viewId).build()
}
}
}
Expand Up @@ -18,7 +18,7 @@ import com.bytedance.scenedemo.group.viewpager.ViewPagerSample
/**
* Created by JiangQi on 9/5/18.
*/
class FragmentBindingDemoActivity : AppCompatActivity() {
class NavigationSceneBindToFragmentSample : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportFragmentManager.beginTransaction().add(android.R.id.content, AFragment()).commitNow()
Expand Down
1 change: 1 addition & 0 deletions demo/src/main/res/layout/parent_scene_viewpager.xml
Expand Up @@ -3,6 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_green_light"
android:id="@+id/scene_root"
android:orientation="vertical">

<com.bytedance.scene.ui.view.StatusBarView
Expand Down
1 change: 1 addition & 0 deletions demo/src/main/res/values-zh/strings.xml
Expand Up @@ -100,6 +100,7 @@
<string name="main_part_btn_async_inflate">异步 Inflate</string>
<string name="main_part_btn_bind_navigationscene_to_fragment">NavigationScene 绑定到 Fragment</string>
<string name="main_part_btn_bind_groupscene_to_activity">GroupScene 绑定到 Activity</string>
<string name="main_part_btn_bind_groupscene_to_fragment">GroupScene 绑定到 Fragment</string>
<string name="main_part_btn_bind_to_view">绑定到 View</string>
<string name="main_part_btn_floating_window">悬浮窗(未完成)</string>
<string name="main_part_btn_inherited_scene">继承的 Scene</string>
Expand Down
1 change: 1 addition & 0 deletions demo/src/main/res/values/strings.xml
Expand Up @@ -51,6 +51,7 @@
<string name="main_part_btn_scene_place_holder_view">ScenePlaceHolderView(xml)</string>
<string name="main_part_btn_bind_navigationscene_to_fragment">Bind NavigationScene to Fragment</string>
<string name="main_part_btn_bind_groupscene_to_activity">Bind GroupScene to Activity</string>
<string name="main_part_btn_bind_groupscene_to_fragment">Bind GroupScene to Fragment</string>
<string name="main_part_btn_bind_to_view">Bind to View</string>
<string name="main_part_btn_migrate_from_classic_android_app">Migrate from classic Android app</string>
<string name="main_part_btn_async_inflate">Async Inflate</string>
Expand Down
@@ -0,0 +1,208 @@
/*
* Copyright (C) 2019 ByteDance Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.bytedance.scene.ui;

import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import com.bytedance.scene.*;
import com.bytedance.scene.group.GroupScene;
import com.bytedance.scene.utlity.SceneInstanceUtility;
import com.bytedance.scene.utlity.ThreadUtility;
import com.bytedance.scene.utlity.Utility;

import java.util.HashSet;
import java.util.WeakHashMap;

public class GroupSceneCompatUtility {
private GroupSceneCompatUtility() {
}

public static final class Builder {
@NonNull
private final Fragment mFragment;
@NonNull
private final Class<? extends GroupScene> mRootSceneClazz;
@Nullable
private Bundle mRootSceneArguments;
@IdRes
private final int mIdRes;
private boolean mSupportRestore = false;
@Nullable
private SceneComponentFactory mRootSceneComponentFactory;
@NonNull
private String mTag = NavigationSceneCompatUtility.LIFE_CYCLE_FRAGMENT_TAG;
private boolean mImmediate = true;

private Builder(@NonNull Fragment fragment, @NonNull Class<? extends GroupScene> rootSceneClazz, @IdRes int containerId) {
this.mFragment = Utility.requireNonNull(fragment, "Fragment can't be null");
this.mRootSceneClazz = Utility.requireNonNull(rootSceneClazz, "Root Scene class can't be null");
this.mIdRes = containerId;
}

@NonNull
public GroupSceneCompatUtility.Builder rootSceneArguments(@Nullable Bundle rootSceneArguments) {
this.mRootSceneArguments = rootSceneArguments;
return this;
}

@NonNull
public GroupSceneCompatUtility.Builder supportRestore(boolean supportRestore) {
this.mSupportRestore = supportRestore;
return this;
}

@NonNull
public GroupSceneCompatUtility.Builder rootSceneComponentFactory(@Nullable SceneComponentFactory rootSceneComponentFactory) {
this.mRootSceneComponentFactory = rootSceneComponentFactory;
return this;
}

@NonNull
public GroupSceneCompatUtility.Builder tag(@NonNull String tag) {
this.mTag = Utility.requireNonNull(tag, "Tag can't be null");
return this;
}

@NonNull
public GroupSceneCompatUtility.Builder immediate(boolean immediate) {
this.mImmediate = immediate;
return this;
}

@NonNull
public GroupSceneDelegate build() {
return setupWithFragment(this.mFragment, this.mIdRes, this.mRootSceneClazz, this.mRootSceneArguments, this.mRootSceneComponentFactory, this.mSupportRestore, this.mTag, this.mImmediate);
}
}

@NonNull
public static GroupSceneCompatUtility.Builder setupWithFragment(@NonNull final Fragment fragment,
@NonNull Class<? extends GroupScene> rootScene,
@IdRes int containerId) {
return new GroupSceneCompatUtility.Builder(fragment, rootScene, containerId);
}

@NonNull
private static GroupSceneDelegate setupWithFragment(@NonNull final Fragment fragment,
@IdRes int containerId,
@NonNull Class<? extends Scene> sceneClazz,
@Nullable Bundle sceneArguments,
@Nullable SceneComponentFactory rootSceneComponentFactory,
final boolean supportRestore,
@NonNull final String tag,
final boolean immediate) {
ThreadUtility.checkUIThread();
if (tag == null) {
throw new IllegalArgumentException("tag cant be null");
}
NavigationSceneCompatUtility.checkDuplicateTag(fragment, tag);

final FragmentManager fragmentManager = fragment.getChildFragmentManager();
LifeCycleCompatFragment lifeCycleFragment = (LifeCycleCompatFragment) fragmentManager.findFragmentByTag(tag);
if (lifeCycleFragment != null && !supportRestore) {
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.remove(lifeCycleFragment);
FragmentUtility.commitFragment(transaction, immediate);
lifeCycleFragment = null;
}

ViewFinder viewFinder = new FragmentViewFinder(fragment);
GroupScene groupScene = null;
if (rootSceneComponentFactory != null) {
groupScene = (GroupScene) rootSceneComponentFactory.instantiateScene(fragment.getClass().getClassLoader(), sceneClazz.getName(), sceneArguments);
}
if (groupScene == null) {
groupScene = (GroupScene) SceneInstanceUtility.getInstanceFromClass(sceneClazz, sceneArguments);
}

ScopeHolderCompatFragment targetScopeHolderFragment = null;
SceneLifecycleDispatcher<GroupScene> dispatcher = null;
if (lifeCycleFragment != null) {
final ScopeHolderCompatFragment scopeHolderFragment = ScopeHolderCompatFragment.install(fragment, tag, false, immediate);
targetScopeHolderFragment = scopeHolderFragment;

dispatcher = new SceneLifecycleDispatcher<>(containerId, viewFinder, groupScene, scopeHolderFragment, supportRestore);
lifeCycleFragment.setSceneContainerLifecycleCallback(dispatcher);
} else {
final ScopeHolderCompatFragment scopeHolderFragment = ScopeHolderCompatFragment.install(fragment, tag, !supportRestore, immediate);
lifeCycleFragment = LifeCycleCompatFragment.newInstance(supportRestore);

dispatcher = new SceneLifecycleDispatcher<>(containerId, viewFinder, groupScene, scopeHolderFragment, supportRestore);
lifeCycleFragment.setSceneContainerLifecycleCallback(dispatcher);

FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(containerId, lifeCycleFragment, tag);
FragmentUtility.commitFragment(transaction, immediate);
targetScopeHolderFragment = scopeHolderFragment;
}

final LifeCycleCompatFragment finalLifeCycleFragment = lifeCycleFragment;
final ScopeHolderCompatFragment finalTargetScopeHolderFragment = targetScopeHolderFragment;
final GroupScene finalGroupScene = groupScene;
final GroupSceneDelegate proxy = new GroupSceneDelegate() {
private boolean mAbandoned = false;

@Nullable
@Override
public GroupScene getGroupScene() {
if (this.mAbandoned) {
return null;
}
return finalGroupScene;
}

@Override
public void abandon() {
if (this.mAbandoned) {
return;
}
this.mAbandoned = true;
final View view = finalGroupScene.getView();
FragmentTransaction transaction = fragmentManager.beginTransaction().remove(finalLifeCycleFragment).remove(finalTargetScopeHolderFragment);
if (immediate) {
fragmentManager.registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentDetached(FragmentManager fm, Fragment f) {
super.onFragmentDetached(fm, f);
if (f != finalLifeCycleFragment) {
return;
}
fragmentManager.unregisterFragmentLifecycleCallbacks(this);
NavigationSceneCompatUtility.removeTag(fragment, tag);
}
}, false);
FragmentUtility.commitFragment(transaction, true);
if (view != null) {
Utility.removeFromParentView(view);
}
} else {
FragmentUtility.commitFragment(transaction, false);
NavigationSceneCompatUtility.removeTag(fragment, tag);
if (view != null) {
Utility.removeFromParentView(view);
}
}
}
};
return proxy;
}
}
Expand Up @@ -38,7 +38,7 @@
* Created by JiangQi on 9/4/18.
*/
public final class NavigationSceneCompatUtility {
private static final String LIFE_CYCLE_FRAGMENT_TAG = "LifeCycleCompatFragment";
static final String LIFE_CYCLE_FRAGMENT_TAG = "LifeCycleCompatFragment";
private static final WeakHashMap<Fragment, HashSet<String>> CHECK_DUPLICATE_TAG_MAP = new WeakHashMap<>();

private NavigationSceneCompatUtility() {
Expand Down Expand Up @@ -306,7 +306,7 @@ public void onFragmentDetached(FragmentManager fm, Fragment f) {
return;
}
fragmentManager.unregisterFragmentLifecycleCallbacks(this);
CHECK_DUPLICATE_TAG_MAP.get(fragment).remove(tag);
removeTag(fragment, tag);
if (view != null) {
Utility.removeFromParentView(view);
}
Expand All @@ -315,7 +315,7 @@ public void onFragmentDetached(FragmentManager fm, Fragment f) {
FragmentUtility.commitFragment(transaction, true);
} else {
FragmentUtility.commitFragment(transaction, false);
CHECK_DUPLICATE_TAG_MAP.get(fragment).remove(tag);
removeTag(fragment, tag);
if (view != null) {
Utility.removeFromParentView(view);
}
Expand All @@ -325,7 +325,7 @@ public void onFragmentDetached(FragmentManager fm, Fragment f) {
return proxy;
}

private static void checkDuplicateTag(@NonNull Fragment fragment, @NonNull String tag) {
static void checkDuplicateTag(@NonNull Fragment fragment, @NonNull String tag) {
if (CHECK_DUPLICATE_TAG_MAP.get(fragment) != null && CHECK_DUPLICATE_TAG_MAP.get(fragment).contains(tag)) {
throw new IllegalArgumentException("tag duplicate, use another tag when invoke setupWithActivity for the second time in same Fragment");
} else {
Expand All @@ -337,4 +337,8 @@ private static void checkDuplicateTag(@NonNull Fragment fragment, @NonNull Strin
set.add(tag);
}
}

static void removeTag(@NonNull Fragment fragment, @NonNull String tag) {
CHECK_DUPLICATE_TAG_MAP.get(fragment).remove(tag);
}
}

0 comments on commit 3be4397

Please sign in to comment.