Writing Android Native View Based on TBS to provide a summary of React Navtive usage

Keywords: Android SDK React Gradle

Writing Android Native View Based on TBS to provide a summary of React Navtive usage

Integration process of TBS

1. Download SDK and place jar packages in app/libs / directory (Project presentation)
2. Copy liblbs.so in Demo Project to main/jniLibs/armeabi/directory
3. Add NDK support in build.gradle

ndk {
    abiFilters "armeabi-v7a", "x86", "armeabi"
}

4. Adding the required permissions

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

Writing of TBS Android native plug-in

  • Provide React Native usage in native module mode

How to write RN native plug-ins is not demonstrated here, you can refer to the official website.
In fact, it's the RN end that jumps to the native Activity loading TBS view.
In the project, TbsReaderView is used to load PDF, Word and other files, WebView is used to load videos and SDK is used to play videos.

1. Initialization of Tbs

//Initialization in onCreate() method of MainApplication
private void initTBS() {
    //Initialize the X5 kernel
    QbSdk.initX5Environment(this, new QbSdk.PreInitCallback() {
        @Override
        public void onCoreInitFinished() {
            //The x5 kernel initialization completes the callback interface, which calls back and indicates that x5 has been loaded. It may fail to load the x5 kernel and switch to the system kernel under special circumstances.
            Log.e("QbSdk","onCoreInitFinished:");
        }

        @Override
        public void onViewInitFinished(boolean result) {
            //The callback of the x5 kernel initialization indicates that the x5 kernel was loaded successfully for true, otherwise it means that the x5 kernel failed to load and will automatically switch to the system kernel.
            Log.e("QbSdk","Successful loading of the kernel:"+ result);
            isLoadX5 = result;
        }
    });
    
    // Monitor downloads of the X5 kernel
    QbSdk.setTbsListener(new TbsListener() {
        @Override
        public void onDownloadFinish(int i) {
            Log.e("QbSdk","onDownloadFinish:"+ i);
        }

        @Override
        public void onInstallFinish(int i) {
            Log.e("QbSdk","onInstallFinish:"+ i);
        }

        @Override
        public void onDownloadProgress(int i) {
            Log.e("QbSdk","onDownloadProgress:"+ i);
        }
    });
}

2. Using TbsReaderView to load PDF, World and other files in activity

public class TbsActivity extends AppCompatActivity {

    private TbsReaderView mTbsReaderView;
    private RelativeLayout mRelativeLayout;
    private TextView mTextView;
    private Button backButton;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tbs);
        mTbsReaderView = new TbsReaderView(this, new TbsReaderView.ReaderCallback() {
            @Override
            public void onCallBackAction(Integer integer, Object o, Object o1) {
                Log.i("onCallBackAction", "o = " + o + " o1 = " + o1);
            }
        });
        backButton = findViewById(R.id.tbs_back_button);
        backButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        mRelativeLayout = findViewById(R.id.tbsRelativeView);
        mRelativeLayout.addView(mTbsReaderView,new RelativeLayout.LayoutParams(-1,-1));
        mTextView = findViewById(R.id.tbs_title);
        String localPath = getIntent().getStringExtra("localPath");
        displayFile(localPath);
        mTextView.setText(FileUtils.fileName(localPath));
    }

    private void displayFile(String localPath) {
        if (!MainApplication.getInstance().getLoadX5()) {
            Toast.makeText(TbsActivity.this, "Load X5 Kernel failure", Toast.LENGTH_SHORT).show();
            return;
        }

        if (localPath.length() == 0 || localPath == null) {
            Toast.makeText(TbsActivity.this, "Failed to obtain filename", Toast.LENGTH_SHORT).show();
            return;
        }
        Bundle bundle = new Bundle();
        String tempPath =  Environment.getExternalStorageDirectory()
                .getPath();
        String filePath = localPath;
        int index = localPath.indexOf("file:///");
        if (index != -1) {
            filePath = localPath.substring(localPath.indexOf("/") + 2);
        }
        Log.e("displayFile - filePath", filePath);
        Boolean isExists = FileUtils.fileIsExists(filePath);
        if (!isExists) {
            Toast.makeText(TbsActivity.this, "Local file does not exist", Toast.LENGTH_SHORT).show();
            return;
        }
        bundle.putString("filePath", filePath);
        bundle.putString("tempPath",tempPath);
        boolean result = mTbsReaderView.preOpen(parseFormat(localPath), false);
        if (result) {
            mTbsReaderView.openFile(bundle);
        } else {
            Toast.makeText(TbsActivity.this, "Preview file failed", Toast.LENGTH_SHORT).show();
        }
    }

    private String parseFormat(String fileName) {
        return fileName.substring(fileName.lastIndexOf(".") + 1);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mTbsReaderView.onStop();
    }
}

3. Playing video with WebView, but this way will not play automatically

public class TbsVideoActivity extends AppCompatActivity {

    private X5WebView webView;
    private Button backButton;
    private TextView mTextView;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tbs_video);
        webView = findViewById(R.id.video_webView);
        mTextView = findViewById(R.id.tbs_title);
        backButton = findViewById(R.id.tbs_back_button);
        backButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        initDisplayVideoView();
    }

    private void initDisplayVideoView() {
        if (!MainApplication.getInstance().getLoadX5()) {
            Toast.makeText(TbsVideoActivity.this, "Load X5 Kernel failure", Toast.LENGTH_SHORT).show();
            return;
        }
        String localPath = getIntent().getStringExtra("localPath");
        mTextView.setText(FileUtils.fileName(localPath));
        Log.i("TbsVideoActivity", localPath);
        if (localPath.length() == 0 || localPath == null) {
            Toast.makeText(TbsVideoActivity.this, "Failed to obtain filename", Toast.LENGTH_SHORT).show();
            return;
        }
        Boolean isExists = FileUtils.fileIsExists(localPath);
        if (!isExists) {
            Toast.makeText(TbsVideoActivity.this, "Local file does not exist", Toast.LENGTH_SHORT).show();
            return;
        }
        if (localPath.indexOf("file://") == -1) {
            localPath = "file://" + localPath;
        }
        webView.loadUrl(localPath);
        getWindow().setFormat(PixelFormat.TRANSLUCENT);
        webView.getView().setOverScrollMode(View.OVER_SCROLL_ALWAYS);
        webView.setWebChromeClient(new com.tencent.smtt.sdk.WebChromeClient());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (webView != null) {
            webView.onPause();
        }
    }
}

4. Automatically Playing Video with Activity in SDK

activyty To configure
<activity
    android:name="com.tencent.smtt.sdk.VideoActivity"
    android:alwaysRetainTaskState="true"
    android:configChanges="orientation|screenSize|keyboardHidden"
    android:exported="false"
    android:launchMode="singleTask">
    <intent-filter>
        <action android:name="com.tencent.smtt.tbs.video.PLAY" />

        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

// Play video
public void autoPreviewVideoByTBSForRN(String localPath) {
    Log.i("autoPlayVideo", localPath);
    if (localPath.length() == 0) {
        Toast.makeText(mReactContext, "Failed to obtain filename", Toast.LENGTH_SHORT).show();
        return;
    }
    Boolean isExists = FileUtils.fileIsExists(localPath);
    if (!isExists) {
        Toast.makeText(mReactContext, "Local file does not exist", Toast.LENGTH_SHORT).show();
        return;
    }
    if (localPath.indexOf("file://") == -1) {
        localPath = "file://" + localPath;
    }
    if (TbsVideo.canUseTbsPlayer(mReactContext)){
        //Play video
        TbsVideo.openVideo(mReactContext, localPath);
    } else {
        Toast.makeText(mReactContext, "Broadcast failure", Toast.LENGTH_SHORT).show();
    }
}
  • Preview PDF, Word and other files by providing native View view

Problem encountered: Native View cannot be refreshed in RN and onLayout overridden in ReactRootView is empty implementation

public class TbsPreviewView extends RelativeLayout {

    private TbsReaderView mTbsReaderView;
    private RelativeLayout mRelativeLayout;
    private Context mContext;
    public String localPath;

    public TbsPreviewView(Context context) {
        super(context);
        LayoutInflater.from(context).inflate(R.layout.tbs_preview, this);
        mContext = context;
        mRelativeLayout = findViewById(R.id.tbs_preview_relativeView);
        mTbsReaderView = new TbsReaderView(MainApplication.getInstance().getMainActivity(), new TbsReaderView.ReaderCallback() {
            @Override
            public void onCallBackAction(Integer integer, Object o, Object o1) {
                Log.i("onCallBackAction", "o = " + o + " o1 = " + o1);
            }
        });
        mRelativeLayout.addView(mTbsReaderView,new RelativeLayout.LayoutParams(-1,-1));
    }

    public void setLocalPath(String localPath) {
        this.localPath = localPath;
        if (localPath.length() != 0) {
            Log.i("TbsReaderView", "displayFile setLocalPath = " + localPath);
            displayFile(localPath);
        }
    }

    private void displayFile(String localPath) {
        if (!MainApplication.getInstance().getLoadX5()) {
            Toast.makeText(mContext, "Load X5 Kernel failure", Toast.LENGTH_SHORT).show();
            return;
        }

        if (localPath.length() == 0) {
            Toast.makeText(mContext, "Failed to obtain filename", Toast.LENGTH_SHORT).show();
            return;
        }
        Bundle bundle = new Bundle();
        String tempPath =  Environment.getExternalStorageDirectory()
                .getPath();

        String filePath = localPath;
        int index = localPath.indexOf("file:///");
        if (index != -1) {
            filePath = localPath.substring(localPath.indexOf("/") + 2);
        }
        Log.e("displayFile - filePath", filePath);
        Boolean isExists = FileUtils.fileIsExists(filePath);
        if (!isExists) {
            Toast.makeText(mContext, "Local file does not exist", Toast.LENGTH_SHORT).show();
            return;
        }
        bundle.putString("filePath", filePath);
        bundle.putString("tempPath",tempPath);
        boolean result = mTbsReaderView.preOpen(parseFormat(localPath), false);
        if (result) {
            mTbsReaderView.openFile(bundle);
        } else {
            Toast.makeText(mContext, "Preview file failed", Toast.LENGTH_SHORT).show();
        }
    }

    private String parseFormat(String fileName) {
        return fileName.substring(fileName.lastIndexOf(".") + 1);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Log.i("TbsPreviewView", "onDetachedFromWindow");
        if (mTbsReaderView != null) {
            mTbsReaderView.onStop();
        }
    }

    /**
     *
     * Empty implementation in ReactRootView
     * protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}
     *
     */
    @Override
    public void requestLayout() {
        super.requestLayout();
        if (getWidth() > 0 && getHeight() > 0) {
            int w = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY);
            int h = MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY);
            measure(w, h);
            layout(getPaddingLeft() + getLeft(), getPaddingTop() + getTop(), getWidth() + getPaddingLeft() + getLeft(), getHeight() + getPaddingTop() + getTop());
        }
    }
}

// RN end use
render() {
    return (
        <View style={styles.container}>
            <TbsPreviewView style={styles.previewViewStyle} 
            localPath={this.state.localPath}/>
        </View>
    )
}

Problems in this way: Preview related documents will have the problem of failure exception, instability, it is better to use RN to jump native Activity.

Full Code Address

Posted by lmhart on Tue, 16 Apr 2019 21:39:33 -0700