All Products
Search
Document Center

Mobile Platform as a Service:Function configuration

Last Updated:Aug 29, 2024

Support for experience edition and non-experience edition

  1. Enable the function.

    Mriver.setConfig("mr_experience_required", "YES");
  2. Open the mini program experience/test version.

    MriverResource.de leteApp(appId); // Delete the local version.
    
    Bundle bundle = new Bundle();
    bundle.putString(RVStartParams.LONG_NB_UPDATE, "synctry"); // The version is forcibly updated. You can use this parameter in combination.
    bundle.putInt(RVStartParams.LONG_NB_EXPERIENCE_REQUIRED, 1); // 1 indicates the experience version. If no parameter is passed, the official version is used.
    Mriver.startApp(this, appId, bundle);

Support mini program page return query dialog box

  1. Turn on the return interception switch.

    Mriver.setConfig("enable_back_perform", "YES");
  2. Upgrade the Appx version.

    // Add the value to build.gradle.
    api ('com.mpaas.mriver:mriverappxplus-build:2.7.18.20230130001@aar') {
          force=true
    }
  3. Call related APIs during mini program development.

    // Enable basic retouching.
    my.enableAlertBeforeUnload({
          message: 'Are you sure you want to leave this page?',
    });
    
    // Disable basic retouching.
    my.disableAlertBeforeUnload()

Real machine debug /preview

MriverDebug.setWssHost("Real WSS address");
MriverDebug.debugAppByScan(activity);

Custom title bar

Mriver.setProxy(TitleViewFactoryProxy.class, new TitleViewFactoryProxy() {
                @Override
                public ITitleView createTitle(Context context, App app) {
                    return new CustomTitleView(context);
                }
            });

public class CustomTitleView implements ITitleView, View.OnClickListener {
    
    public static final String TAG = MRConstants.INTEGRATION_TAG + ":MRTitleView";
    protected TextView tvTitle;
    protected ImageView ivImageTitle;
    protected ImageView btBack;
    protected TextView btBackToHome;
    protected RelativeLayout rlTitle;
    protected View statusBarAdjustView;
    
    protected List<ImageButton> btIconList = new ArrayList<>();
    
    
    // The container view of the entire TitleBar.
    protected TitleBarFrameLayout contentView;
    
    // The number of OptionMenu items in the upper-right corner. Default value: 1.
    protected int visibleOptionNum;
    protected Page mPage;
    
    // Bottom line separator
    protected View mDivider;
    protected Context mContext;
    
    protected TitleViewIconSpec mTitleViewIconSpec;
    
    protected TitleViewStyleSpec mDarkStyleSpec;
    
    protected TitleViewStyleSpec mLightStyleSpec;
    
    //    protected  ProgressBar mNavLoadingBar;
    protected ITitleEventDispatcher mTitleEventDispatcher;
    
    public CustomTitleView(Context context) {
        mContext = context;
        ViewGroup parent = null;
        if (context instanceof Activity && ((Activity) context).getWindow() != null) {
            parent = ((Activity) mContext).findViewById(android.R.id.content);
        }
        
        mTitleViewIconSpec = TitleViewSpecProvider.g().getIconSpec();
        mDarkStyleSpec = TitleViewSpecProvider.g().getDarkSpec();
        mLightStyleSpec = TitleViewSpecProvider.g().getLightSpec();
        
        contentView = (TitleBarFrameLayout) LayoutInflater.from(context).inflate(R.layout.mriver_title_bar_demo, parent, false);
        tvTitle = contentView.findViewById(R.id.h5_tv_title);
        ivImageTitle = contentView.findViewById(R.id.h5_tv_title_img);
        statusBarAdjustView = contentView.findViewById(R.id.h5_status_bar_adjust_view);
        ivImageTitle.setVisibility(View.GONE);
        tvTitle.setOnClickListener(this);
        ivImageTitle.setOnClickListener(this);
        
        btBack = contentView.findViewById(R.id.h5_tv_nav_back);
        btBackToHome = contentView.findViewById(R.id.h5_tv_nav_back_to_home);
        
        
        mDivider = contentView.findViewById(R.id.h5_h_divider_intitle);
        
        rlTitle = contentView.findViewById(R.id.h5_rl_title);
        visibleOptionNum = 1;
        
        // ad view
        //        adViewLayout.setTag(H5Utils.TRANSPARENT_AD_VIEW_TAG);
        
        btBack.setOnClickListener(this);
        btBackToHome.setOnClickListener(this);
        
        applyViewStyleAndIcon();
        
    }
    
    protected void applyViewStyleAndIcon() {
        boolean useBackSpec = false;
        boolean useHomeSpec = false;
        if (mTitleViewIconSpec != null) {
            TitleViewIconSpec.IconSpecEntry btHomeSpec = mTitleViewIconSpec.getHomeButton();
            if (btHomeSpec != null) {
                btBackToHome.setTypeface(btHomeSpec.getKey());
                btBackToHome.setText(btHomeSpec.getValue());
                useHomeSpec = true;
            }
            
        }
        
        if (!useHomeSpec) {
            Typeface iconFont = Typeface.createFromAsset(mContext.getAssets(), "mrv_iconfont.ttf");
            btBackToHome.setTypeface(iconFont);
        }
        
        btBackToHome.setTextColor(StateListUtils.getStateColor(mLightStyleSpec.getHomeButtonColor()));
    }
    
    
    protected void setButtonIcon(Bitmap btIcon, int index) {
        if (isOutOfBound(index, btIconList.size())) {
            return;
        }
        btIconList.get(index).setImageBitmap(btIcon);
    }
    
    @Override
    public void setTitle(String title) {
        if (title != null && enableSetTitle(title)) {
            tvTitle.setText(title);
            tvTitle.setVisibility(View.VISIBLE);
            ivImageTitle.setVisibility(View.GONE);
        }
    }
    
    protected boolean enableSetTitle(String title) {
        return !title.startsWith("http://") && !title.startsWith("https://");
    }
    
    // view visible control
    protected boolean isOutOfBound(int num, int length) {
        return length == 0 || length < num;
    }
    
    @Override
    public void showBackButton(boolean show) {
        btBack.setVisibility(show ? View.VISIBLE : View.GONE);
        if (show && btBackToHome != null) {
            btBackToHome.setVisibility(View.GONE);
        }
        addLeftMarginOnTitle();
    }
    
    @Override
    public void showOptionMenu(boolean b) {
        
    }
    
    public void showHomeButton(boolean show) {
        btBackToHome.setVisibility(show ? View.VISIBLE : View.GONE);
        if (show) {
            btBack.setVisibility(View.GONE);
        }
        addLeftMarginOnTitle();
    }
    
    @Override
    public void setTitleEventDispatcher(ITitleEventDispatcher dispatcher) {
        mTitleEventDispatcher = dispatcher;
    }
    
    @Override
    public void addCapsuleButtonGroup(View view) {
        if (view == null) {
            return;
        }
    }
    
    protected void addLeftMarginOnTitle() {
        boolean needAdd = btBack.getVisibility() != View.VISIBLE &&
            btBackToHome.getVisibility() != View.VISIBLE;
        RelativeLayout.LayoutParams rlTitleLayoutParams =
            (RelativeLayout.LayoutParams) rlTitle.getLayoutParams();
        rlTitleLayoutParams.setMargins(!needAdd ? 0 : DimensionUtil.dip2px(mContext, 16), 0, 0, 0);
        
    }
    
    @Override
    public void showTitleLoading(boolean show) {
    }
    
    @Override
    public View getContentView() {
        return contentView;
    }
    
    @Override
    public void onClick(View view) {
        RVLogger.d(TAG, "onClick " + view);
        if (mPage == null) {
            return;
        }
        if (view.equals(btBack)) {
            if (mTitleEventDispatcher != null) {
                mTitleEventDispatcher.onBackPressed();
            }
        } else if (view.equals(tvTitle) || view.equals(ivImageTitle)) {
            if (mTitleEventDispatcher != null) {
                mTitleEventDispatcher.onTitleClick();
            }
        } else if (view.equals(btBackToHome)) {
            if (mTitleEventDispatcher != null) {
                mTitleEventDispatcher.onHomeClick();
            }
        }
    }
    
    @Override
    public void setPage(Page page) {
        mPage = page;
        tvTitle.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                mPage.getApp().restartFromServer(null);
                return false;
            }
        });
    }
    
    
    public View getDivider() {
        return mDivider;
    }
    
    protected void switchToLightTheme() {
        tvTitle.setTextColor(mLightStyleSpec.getTitleTextColor());
        
        btBackToHome.setTextColor(StateListUtils.getStateColor(mLightStyleSpec.getHomeButtonColor()));
        
    }
    
    protected void switchToDarkTheme() {
        tvTitle.setTextColor(mDarkStyleSpec.getTitleTextColor());
        
        btBackToHome.setTextColor(StateListUtils.getStateColor(mDarkStyleSpec.getHomeButtonColor()));
        
        
    }
    
    public void onRelease() {
        btIconList.clear();
    }
    
    
    /***
    * Turn on immersive status bar support
    */
    @Override
    public void setStatusBarColor(int color) {
        if (StatusBarUtils.isSupport()) {
            int statusBarHeight = StatusBarUtils.getStatusBarHeight(mContext);
            
            If (statusBarHeight == 0) { // protection, in case rom cannot get the height of the status bar, it will not take effect here. 
                return;
            }
            LinearLayout.LayoutParams layoutParams =
                (LinearLayout.LayoutParams) statusBarAdjustView.getLayoutParams();
            layoutParams.height = statusBarHeight;
            statusBarAdjustView.setLayoutParams(layoutParams);
            statusBarAdjustView.setVisibility(View.VISIBLE);
            
            try {
                StatusBarUtils.setTransparentColor((Activity) mContext, color);
            } catch (Exception e) {
                RVLogger.e(TAG, e);
            }
        }
    }
    
    @Override
    public void setBackgroundColor(int color) {
        contentView.getContentBgView().setColor(color);
    }
    
    @Override
    public void setAlpha(int alpha, boolean titleTextAlphaEnabled) {
        contentView.getContentBgView().setAlpha(alpha);
        if (titleTextAlphaEnabled) {
            tvTitle.setAlpha(alpha);
        }
    }
    
    @Override
    public void setOptionMenu(Bitmap bitmap) {
        visibleOptionNum = 2;
        setButtonIcon(bitmap, 1);
    }
    
    @Override
    public void setTitleImage(Bitmap image, String contentDesc) {
        if (!TextUtils.isEmpty(contentDesc)) {
            ivImageTitle.setContentDescription(contentDesc);
        }
        if (image != null) {
            RVLogger.d(TAG, "imgTitle width " + image.getWidth() + ", imgTitle height " + image
                       .getHeight());
            ivImageTitle.setImageBitmap(image);
            ivImageTitle.setVisibility(View.VISIBLE);
            tvTitle.setVisibility(View.GONE);
            RVLogger.d(TAG, "ivImageTitle width " + ivImageTitle
                       .getWidth() + ", ivImageTitle height " + ivImageTitle.getHeight());
        }
    }
    
    @Override
    public void setTitlePenetrate(boolean enable) {
        contentView.setPreventTouchEvent(!enable);
    }
    
    @Override
    public void applyTheme(TitleBarTheme theme) {
        if (theme == TitleBarTheme.DARK) {
            switchToDarkTheme();
        } else if (theme == TitleBarTheme.LIGHT) {
            switchToLightTheme();
        }
    }
}

Custom mini program loading animation

Mriver.setProxy(SplashViewFactoryProxy.class, new SplashViewFactoryProxy() {

                @Override
                public ISplashView createSplashView(Context context) {
                    return new CustomLoadingView(context);
                }
            });

public class CustomLoadingView extends FrameLayout implements ISplashView {

    private static final String TAG = "CustomLoadingView";

    private static final int defaultAlphaColor=855638016;//Color.argb(51, 0, 0, 0);// Default transparent color
    private static final long TIME_DELAY_FOR_SHOW_PERCENTAGE=2000; // Show the percentage of the delay time 2s.

    public final static String MSG_UPDATE_APPEARANCE = "UPDATE_APPEARANCE";
    public final static String DATA_UPDATE_APPEARANCE_BG_COLOR = "UPDATE_APPEARANCE_BG_COLOR"; // Page background color# RGB
    public final static String DATA_UPDATE_APPEARANCE_LOADING_ICON = "UPDATE_APPEARANCE_LOADING_ICON"; // Loading icon Drawable
    public final static String DATA_UPDATE_APPEARANCE_LOADING_TEXT = "UPDATE_APPEARANCE_LOADING_TEXT"; //loading copy
    public final static String DATA_UPDATE_APPEARANCE_LOADING_TEXT_COLOR = "UPDATE_APPEARANCE_LOADING_TEXT_COLOR"; //loading copy color# RGB
    public final static String DATA_UPDATE_APPEARANCE_LOADING_BOTTOM_TIP = "UPDATE_APPEARANCE_LOADING_BOTTOM_TIP"; // Prompt copy at bottom

    public final static String ANIMATION_STOP_LOADING_PREPARE = "ANIMATION_STOP_LOADING_PREPARE";

    private Context mContext;

    protected ImageView mLoadingIcon;
    protected TextView mLoadingTitle;
    protected TextView mLoadingPercentTip;
    protected TextView mBottomTip;
    protected TextView mBackButton;

    private Paint mDotPaint;
    private Timer mTimer;
    private TimerTask mTimerTask;
    private boolean mPlayingStartAnim;
    private int mDarkDotX;
    private int mDarkDotY;
    private int mDarkGap;
    private int mDotSize;
    private int mLightDotIndex = 0;
    private int mPercentValue;
    private long mStartLoadingTime = 0;

    private OnCancelListener onCancelListener;
    private Activity hostActivity;

    public interface OnCancelListener {
        void onCancel();
    }

    public CustomLoadingView(Context context) {
        this(context, null);
    }

    public CustomLoadingView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomLoadingView(final Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mContext = context;

        hostActivity = (Activity) context;

        initView();

        mBackButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                cancel();
                if (context instanceof Activity) {
                    RVLogger.d(TAG, "user want close app when splash loading");
                    ((Activity) context).finish();
                }
            }
        });
    }

    public final void cancel() {
        if (this.onCancelListener != null) {
            this.onCancelListener.onCancel();
        }

    }

    public void initView() {
        mLoadingIcon = new ImageView(mContext);
        mLoadingIcon.setScaleType(ImageView.ScaleType.FIT_XY);
        mLoadingIcon.setImageResource(R.drawable.ic_launcher_foreground);
        mLoadingTitle = new TextView(mContext);
        mLoadingTitle.setGravity(Gravity.CENTER);
        mLoadingTitle.setTextColor(Color.BLACK);
        mLoadingTitle.setSingleLine();
        mLoadingTitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
        mLoadingTitle.setEllipsize(TextUtils.TruncateAt.END);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        mLoadingTitle.setLayoutParams(lp);
        addView(mLoadingIcon);
        addView(mLoadingTitle);

        mBackButton = new TextView(mContext);
        mBackButton.setGravity(Gravity.CENTER);
        addView(mBackButton);

        // Load percentage
        mPercentValue = 0;
        mLoadingPercentTip = new TextView(mContext);
        mLoadingPercentTip.setGravity(Gravity.CENTER);
        mLoadingPercentTip.setSingleLine();
        mLoadingPercentTip.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);
        mLoadingPercentTip.setEllipsize(TextUtils.TruncateAt.END);
        lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        mLoadingPercentTip.setLayoutParams(lp);
        mLoadingPercentTip.setText("");
        addView(mLoadingPercentTip);

        mBottomTip = new TextView(mContext);
        mBottomTip.setTextSize(12);
        mBottomTip.setGravity(Gravity.CENTER);
        lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        mBottomTip.setLayoutParams(lp);
        addView(mBottomTip);

        mDotSize = 30;
        mDotPaint = new Paint();
        mDotPaint.setStyle(Paint.Style.FILL);
        mDarkGap = 10;

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int size = 150;
        mLoadingIcon.measure(makeMeasureSpec(size), makeMeasureSpec(size));

        int height = 200;
        int width = 500;
        mLoadingTitle.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), makeMeasureSpec(height));

        height = 200;
        width = 500;
        mLoadingPercentTip.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), makeMeasureSpec(height));

        width = 200;
        height = 100;
        mBottomTip.measure(makeMeasureSpec(width), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));

        width = 200;
        height = 200;
        mBackButton.measure(makeMeasureSpec(width), makeMeasureSpec(height));

        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int offsetX = 0;
        int offsetY = 0;

        mBackButton.layout(offsetX, offsetY, mBackButton.getMeasuredWidth(), mBackButton.getMeasuredHeight() + offsetY);

        offsetX = (getMeasuredWidth() - mLoadingIcon.getMeasuredWidth()) / 2;
        mLoadingIcon.layout(offsetX, offsetY, offsetX + mLoadingIcon.getMeasuredWidth(),
                offsetY + mLoadingIcon.getMeasuredHeight());

        offsetX = (getMeasuredWidth() - mLoadingTitle.getMeasuredWidth()) / 2;
        offsetY = offsetY + mLoadingIcon.getMeasuredHeight();
        mLoadingTitle.layout(offsetX, offsetY, offsetX + mLoadingTitle.getMeasuredWidth(),
                offsetY + mLoadingTitle.getMeasuredHeight());

        mDarkDotX = getMeasuredWidth() / 2 - mDotSize - mDarkGap;
        mDarkDotY = offsetY + mLoadingTitle.getMeasuredHeight();

        offsetX = (getMeasuredWidth() - mLoadingPercentTip.getMeasuredWidth()) / 2;
        offsetY = offsetY + mLoadingPercentTip.getMeasuredHeight();
        mLoadingPercentTip.layout(offsetX, offsetY, offsetX + mLoadingPercentTip.getMeasuredWidth(),
                offsetY + mLoadingPercentTip.getMeasuredHeight());

        offsetX = (getMeasuredWidth() - mBottomTip.getMeasuredWidth()) / 2;
        offsetY = getMeasuredHeight() - mBottomTip.getMeasuredHeight();
        mBottomTip.layout(offsetX, offsetY, offsetX + mBottomTip.getMeasuredWidth(), offsetY + mBottomTip.getMeasuredHeight());
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (mPlayingStartAnim) {
            mDotPaint.setColor(Color.BLACK);
            mDarkDotX = getMeasuredWidth() / 2 - mDotSize - mDarkGap;
            for (int i = 0; i < 3; i++) {
                mDotPaint.setColor(mLightDotIndex == i ? Color.WHITE : Color.BLACK);
                canvas.drawCircle(mDarkDotX, mDarkDotY, mDotSize / 2, mDotPaint);
                mDarkDotX = mDarkDotX + mDarkGap + mDotSize;
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        super.onTouchEvent(ev);
        return true;
    }

    public void startLoadingAnimation() {
        if (mPlayingStartAnim) return;
        mPlayingStartAnim = true;

        if (mTimerTask == null) {
            mTimerTask = new TimerTask() {
                @Override
                public void run() {
                    mLightDotIndex++;
                    if (mLightDotIndex > 2) {
                        mLightDotIndex = 0;
                    }
                    ExecutorUtils.runOnMain(new Runnable() {
                        @Override
                        public void run() {
                            invalidate();
                            // Update the value of the percentage.
                            if (isCanShowPercentage()) {
                                if (mPercentValue == 0) {
                                    mPercentValue = 52;
                                } else if (mPercentValue < 99) {
                                    mPercentValue++;
                                }
                                mLoadingPercentTip.setText(String.format("%d%%", mPercentValue));
                            }

                        }
                    });
                }
            };
        }

        if (mTimer == null) {
            try {
                mTimer = new Timer();
                mTimer.schedule(mTimerTask, 0, 200);
            } catch (Throwable throwable) {
                RVLogger.e(TAG, "printMonitor error", throwable);
            }
        }

        RVLogger.d(TAG, "SplashLoadingView... startLoading Animation");
    }

    public void stopLoadingAnimation() {
        mPlayingStartAnim = false;

        if (mTimer != null) {
            mTimer.cancel();
        }
        if (mTimerTask != null) {
            mTimerTask.cancel();
        }
        invalidate();

        RVLogger.d(TAG, "SplashLoadingView... stopLoading Animation");
    }

    private int getDimen(int id) {
        return mContext.getResources().getDimensionPixelSize(id);
    }

    private int makeMeasureSpec(int size) {
        return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
    }

    public void onStart() {
        updateStatusBar();
        startLoadingAnimation();
    }

    public void onStop() {
        stopLoadingAnimation();
        mLoadingPercentTip.setVisibility(GONE);
        RVLogger.d(TAG, "SplashLoadingView... stop");
    }

    @Override
    public void onFail() {
        onStop();
        Map<String, Object> msgData = new HashMap<>();
        msgData.put(CustomLoadingView.DATA_UPDATE_APPEARANCE_LOADING_BOTTOM_TIP, "");
        sendMessage(CustomLoadingView.MSG_UPDATE_APPEARANCE, msgData);
    }

    public void onHandleMessage(String msg, Map<String, Object> data) {
        if (MSG_UPDATE_APPEARANCE.equals(msg)) {

            String bgColor = (String) data.get(DATA_UPDATE_APPEARANCE_BG_COLOR);
            if (!TextUtils.isEmpty(bgColor)) {
                setBackgroundColor(Color.parseColor(bgColor));
            }

            Drawable loadingIcon = (Drawable) data.get(DATA_UPDATE_APPEARANCE_LOADING_ICON);
            if (loadingIcon != null) {
                mLoadingIcon.setImageDrawable(loadingIcon);
            }

            String text = (String) data.get(DATA_UPDATE_APPEARANCE_LOADING_TEXT);
            if (text != null) {
                mLoadingTitle.setText(text);
            }

            String textColor = (String) data.get(DATA_UPDATE_APPEARANCE_LOADING_TEXT_COLOR);
            if (!TextUtils.isEmpty(textColor)) {
                mLoadingTitle.setTextColor(Color.parseColor(textColor));
            }

            String bottomTip = (String) data.get(DATA_UPDATE_APPEARANCE_LOADING_BOTTOM_TIP);
            if (bottomTip != null) {
                mBottomTip.setText(bottomTip);
            }
        }
    }

    public void performAnimation(final String animationType, final Animator.AnimatorListener animationListener) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            doPerformAnimation(animationType, animationListener);
        } else {
            post(new Runnable() {
                @Override
                public void run() {
                    doPerformAnimation(animationType, animationListener);
                }
            });
        }
    }

    private void doPerformAnimation(final String animationType, final Animator.AnimatorListener animationListener) {

        if (getParent() == null) {
            RVLogger.e(TAG, "loading view has not added to parent container");
            return;
        }

        if (ANIMATION_STOP_LOADING_PREPARE.equals(animationType)) {
            mPlayingStartAnim = false;

            int offsetTargetY = 0;
            float titleTargetX = 0f;
            if (isBackButtonVisible()) {
                titleTargetX = mBackButton.getX() + mBackButton.getMeasuredWidth();
            } else {
                titleTargetX = getTitleLeftMargin();
            }
            float titleTargetY = (200 - mLoadingTitle.getMeasuredHeight()) / 2;

            AnimatorSet prepareStopLoadingAnimator = new AnimatorSet();
            prepareStopLoadingAnimator.setDuration(400);
            if (animationListener != null) {
                prepareStopLoadingAnimator.addListener(animationListener);
            }
            prepareStopLoadingAnimator.play(ObjectAnimator.ofFloat(mLoadingIcon, "y", mLoadingIcon.getY(), offsetTargetY))
                    .with(ObjectAnimator.ofFloat(mLoadingIcon, "scaleX", mLoadingIcon.getScaleX(), 0))
                    .with(ObjectAnimator.ofFloat(mLoadingIcon, "scaleY", mLoadingIcon.getScaleY(), 0))
                    .with(ObjectAnimator.ofFloat(mLoadingTitle, "x", mLoadingTitle.getX(), titleTargetX))
                    .with(ObjectAnimator.ofFloat(mLoadingTitle, "y", mLoadingTitle.getY(), titleTargetY));

            prepareStopLoadingAnimator.start();
        } else {
            performAnimation(animationType, animationListener);
        }
    }

    @Override
    public void updateLoadingInfo(EntryInfo entryInfo) {
        Map<String, Object> msgData = new HashMap<>();
        msgData.put(CustomLoadingView.DATA_UPDATE_APPEARANCE_LOADING_TEXT, entryInfo.title);

        sendMessage(CustomLoadingView.MSG_UPDATE_APPEARANCE, msgData);

        H5ImageUtil.loadImage(entryInfo.iconUrl, null, new H5ImageListener() {
            @Override
            public void onImage(Bitmap bitmap) {
                RVLogger.d(TAG, "onBitmapLoaded!");
                Map<String, Object> msgData = new HashMap<>();
                int dimen = 100;
                Bitmap displayBitmap = ImageUtil.scaleBitmap(bitmap, dimen, dimen);
                msgData.put(CustomLoadingView.DATA_UPDATE_APPEARANCE_LOADING_ICON, new BitmapDrawable(displayBitmap));
                sendMessage(CustomLoadingView.MSG_UPDATE_APPEARANCE, msgData);
            }
        });
    }

    @Override
    public View getView() {
        return this;
    }

    @Override
    public void onExit() {
        performAnimation(CustomLoadingView.ANIMATION_STOP_LOADING_PREPARE, new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                RVLogger.d(TAG, "onAnimationStart");
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                RVLogger.d(TAG, "onAnimationEnd");
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                RVLogger.d(TAG, "onAnimationCancel");
            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
    }


    private void updateStatusBar() {
        if (hostActivity != null && hostActivity.getClass().getName().equals("com.alipay.mobile.core.loading.impl.LoadingPage")) {
            StatusBarUtils.setTransparentColor(hostActivity, defaultAlphaColor);
        }
    }

    protected boolean isBackButtonVisible() {
        return true;
    }

    protected float getTitleLeftMargin() {
        return 0f;
    }

    private boolean isCanShowPercentage() {
        if (mStartLoadingTime == 0) {
            mStartLoadingTime = System.currentTimeMillis();
        }
        long time = System.currentTimeMillis();
        return ((time - mStartLoadingTime) > TIME_DELAY_FOR_SHOW_PERCENTAGE);
    }

    public final void sendMessage(final String msg, final Map<String, Object> data) {
        this.post(new Runnable() {
            public void run() {
                try {
                    CustomLoadingView.this.onHandleMessage(msg, data);
                } catch (Throwable e) {
                    RVLogger.e(TAG, e);
                }

            }
        });
    }
}

Supports the debug panel

// Call when Mriver is initialized. By default, only the preview and real-machine debugging mini programs are displayed.
MriverEngine.enableDebugConsole();

// Force all mini programs to display the debugging panel.
Mriver.setConfig("mriver_show_debug_menu_all", "YES");

Customize more menu bars

Mriver.setProxy(MRTinyMenuProxy.class, new MRTinyMenuProxy() {
                @Override
                public ITinyMenuPopupWindow createTinyMenuPopupWindow(Context context, TinyMenuViewModel tinyMenuViewModel) {
                    return new DemoTinyMenuPopupWindow(context, tinyMenuViewModel);
                }
            });

// DemoTinyMenuPopupWindow implementation references internal TinyMenuModalWindow 

Listener interception return

Mriver.setConfig("enable_back_perform", "YES");
List<String> tt = new ArrayList<String>();
tt.add(BackInterceptPoint.class.getName());// The name of the interface class.
Mriver.registerPoint(DemoBackInterceptPointProviderImp.class.getName(), tt);


public class DemoBackInterceptPointProviderImp implements BackInterceptPoint {

    @Override
    public boolean intercepted(final Render render, int i, CommonBackPerform.BackHandler backHandler, GoBackCallback goBackCallback) {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(render.getActivity(), "Return key" ,Toast.LENGTH_LONG).show();
            }
        });
        return false; // The value true.
    }

    @Override
    public void onInitialized() {
        Log.i("BackPoint", "BackInterceptPoint--onInitialized--:");
    }

    @Override
    public void onFinalized() {
        Log.i("BackPoint", "BackInterceptPoint--onFinalized--:");
    }
}

Listener and interception disable

Mriver.setProxy(AppCloseInterceptProxy.class, new AppCloseInterceptProxy() {
 @Override
 public boolean intercept(Context context, Page page) {
 showToast("Close key");
 return false; // The value true.
 }
});

Custom appx loading animation (only GIF)

Add "nonLoadingIndicator": false in the mini.project.json file of the mini program.

The code example is as follows:

Mriver.registerPoint(MriverResourceInterceptor.class.getName(),
                    Arrays.asList("com.alibaba.ariver.resource.api.extension.ResourceInterceptPoint"));
            Mriver.setConfig("mriver_custom_appxloading", CUSTOM_LOADING_RESOURCE);
						// Loading size: the proportion of the screen width. 20 means the loading control size is 20% of the screen width.
            Mriver.setConfig("mriver_custom_appxloading_size", "20");
            ResourcePackage resourcePackage = new GlobalResourcePackage("00000001") {
                @Override
                protected boolean needWaitSetupWhenGet() {
                    return false;
                }

                @Override
                public boolean needWaitForSetup() {
                    return false;
                }

                @Override
                protected boolean canHotUpdate(String hotVersion) {
                    return false;
                }

                @Override
                public Resource get(ResourceQuery query) {
                  	// All resources can be intercepted
                    if (TextUtils.equals(CUSTOM_LOADING_RESOURCE, query.pureUrl)) {
                        return getPresetImageResource(UIStyleActivity.this, query);
                    }
                    return null;
                }
            };
GlobalPackagePool.getInstance().add(resourcePackage);

private Resource getPresetImageResource(Context application, ResourceQuery query) {

    Resource sResource = null;
    if (sResource == null) {

        InputStream inputStream = null;
        AssetManager am = application.getAssets();
        try {
            inputStream = am.open("preset/custom_loading.gif");
            int length = inputStream.available();
            byte[] buffer = new byte[length];
            inputStream.read(buffer);
            sResource = new OfflineResource(query.pureUrl, buffer);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }
        }
    }
    return sResource;
}

Resource management

// Delete local mini programs.
MriverResource.deleteApp("xxxx");

// Obtain the information about all mini programs.
Map<String, List<AppModel>> allApp = MriverResource.getAllApp();

// Actively update all
MriverResource.updateAll(new UpdateAppCallback() {
                @Override
                public void onSuccess(List<AppModel> list) {

                    showToast("All information mini programs that can be pulled by userid are updated successfully");
                }

                @Override
                public void onError(UpdateAppException e) {
                    showToast(e.getMessage());
                }
            });

// Actively update a specific mini program.
Map<String, String> updateApp = new HashMap<>();
            updateApp.put("xxx", "");
            MriverResource.updateApp(updateApp, new UpdateAppCallback() {
                @Override
                public void onSuccess(List<AppModel> list) {
                    showToast("The mini program with appid=2021042520210425 is updated successfully");
                }

                @Override
                public void onError(UpdateAppException e) {
                    showToast(e.getMessage());
                }
            });

// Actively download the mini program.
MriverResource.downloadAppPackage("xxx", new PackageDownloadCallback() {
                @Override
                public void onPrepare(String s) {
                    // Do some auxiliary work, such as logging
                }

                @Override
                public void onProgress(String s, int i) {
                    // The progress.
                    showToast("i=" + i);
                }

                @Override
                public void onCancel(String s) {
                    // The user does not need to worry. Cancellation is the cancellation api for the internal network library
                }

                @Override
                public void onFinish(String s) {
                    showToast(s);
                }

                @Override
                public void onFailed(String s, int i, String s1) {
                    showToast("onFailed--" + s);
                }
            });

Preset mini program

  • Put the mini program .amr package and mini program information into the assets/mriver/legacy directory.

    Important

    The file name rule is: appId.amr. Do not include the version number.

    image.png

    The mini program information is put into the nebula_preset.json, and the reference is as follows:

    {
    	"config":{
    		"updateReqRate":16400,
    		"limitReqRate":13600,
    		"appPoolLimit":3,
    		"versionRefreshRate":86400
    	},
    	"data":[
    		{
    			"app_desc":"Preset mini program",
    			"app_id":"2022080915350001",
    			"auto_install":1,
    			"extend_info":{
    				"launchParams":{
    					"enableTabBar":"YES",
    					"enableKeepAlive":"NO",
    					"enableDSL":"YES",
    					"nboffline":"sync",
    					"enableWK":"YES",
    					"page":"page/tabBar/component/index",
    					"tinyPubRes":"YES",
    					"enableJSC":"YES"
    				},
    				"usePresetPopmenu":"YES"
    			},
    			"fallback_base_url":"https://xxx/2022080915350001/1.0.1.0_all/nebula/fallback/",
    			"global_pack_url":"",
    			"installType":1,
    			"main_url":"/index.html#page/tabBar/component/index",
    			"name":"Preset mini program",
    			"online":1,
    			"package_url":"https://xxx/2022080915350001/1.0.1.0_all/nebula/2022080915350001_1.0.1.0.amr",
    			"patch":"",
    			"sub_url":"",
    			"version":"1.0.1.0",
    			"vhost":"https://2022080915350001.h5app.com"
    		}
    	],
    	"resultCode":100,
    	"resultMsg":"The operation is successful.",
    	"state":"success"
    }
  • The following code provides an example on how to install the package in advance.

    private void checkPresetInstalled() {
            Map<String, AppModel> appModelMap = RVProxy.get(RVResourcePresetProxy.class).getPresetAppInfos();
            Map<String, RVResourcePresetProxy.PresetPackage> packageMap = RVProxy.get(RVResourcePresetProxy.class).getPresetPackage();
            Set<String> stringSet = packageMap.keySet();
            for (String key: stringSet) {
                RVResourcePresetProxy.PresetPackage presetPackage = packageMap.get(key);
                AppModel presetModel = appModelMap.get(key);
                if (presetModel != null && presetPackage != null && presetPackage.getInputStream() != null) {
                    AppModel appModel = MriverResource.getAppModel(presetModel.getAppId());
                    boolean available = ((RVResourceManager)RVProxy.get(RVResourceManager.class)).isAvailable(appModel);
                    if (!available) {
                        if (TextUtils.equals(appModel.getAppVersion(), presetModel.getAppVersion())) {
                            InternalUtils.installApp(presetModel, presetPackage.getInputStream());
                        } else {
                            // Determine whether to install the online version in advance.
                        }
                    }
    
                }
            }
        }

Resource load interception

ResourcePackage resourcePackage = new GlobalResourcePackage("00000001") {
                @Override
                protected boolean needWaitSetupWhenGet() {
                    return false;
                }

                @Override
                public boolean needWaitForSetup() {
                    return false;
                }

                @Override
                protected boolean canHotUpdate(String hotVersion) {
                    return false;
                }

                @Override
                public Resource get(ResourceQuery query) {
                  	// All resources can be blocked.
                    if (TextUtils.equals("Specify the resource path", query.pureUrl)) {
                        return getLocalResource(UIStyleActivity.this, query);
                    }
                    return null;
                }
            };
GlobalPackagePool.getInstance().add(resourcePackage);

Signature verification

// Enable the signature.
MriverResource.enableVerify(MriverResource.VERIFY_TYPE_YES,"public key");

// Disable the signature.
MriverResource.disableVerify( );

Enable keep-alive

Mriver.setConfig("enable_keep_alive", "YES");
Mriver.setConfig("mriver_keep_alive_time", "120000"); // The keep-alive time of 2 minutes.
Mriver.setConfig("mriver_keepalive_max", "3"); // Maximum number of keep-alive 3 

Android mini programs are implemented based on the Activity Task Stack. If the mini program jumps to the native page (such as login) or other App pages (such as Third party payment and sharing), note that:

  • There is no risk if it is in the same stack as the mini program page.

  • If these pages are separate activity stacks, the return stack order may be affected and relevant logic needs to be regressed.

By default, if a page is specified during a keep-alive wake-up and the specified page is not the current page of the mini program, the specified page will be refreshed during wake-up.

You can set the refererBiz parameter to ignore the refresh and directly evoke the current page:

 Bundle intent  = new Bundle();
intent.putString("page", "page/component/view/view");
intent.putString("refererBiz", "home");
Mriver.startApp(appId, intent);
Note
  • If the refererBiz value is fixed to home, the specified page is ignored during the keep-alive wake-up. If no mini program is alive, the specified page opens.

  • If the refererBiz value is set to another value, the referer value is compared with the referer value that was opened last time. If the refererBiz value is the same, the specified page is ignored. If no mini program is alive, the specified page opens.

Start a mini program of a specified version

MriverResource.de leteApp("2022080918000001"); // Delete the local mini program.

Bundle bundle = new Bundle();
bundle.putString(RVStartParams.LONG_NB_TARGET_VERSION, "the specified version number");
Mriver.startApp(TargetVersionActivity.this, "2022080918000001", bundle);

Custom JSAPI

// Customize the jsapi of tinyToNative.
MriverEngine.registerBridge(CustomApiBridgeExtension.class);


public class CustomApiBridgeExtension extends SimpleBridgeExtension {

    private static final String TAG = "CustomApiBridgeExtension";

    @ActionFilter
    public void tinyToNative(@BindingId String id,
                             @BindingNode(App.class) App app,
                             @BindingNode(Page.class) Page page,
                             @BindingApiContext ApiContext apiContext,
                             @BindingExecutor(ExecutorType.UI) Executor executor,
                             @BindingRequest JSONObject params,
                             @BindingParam("param1") String param1,
                             @BindingParam("param2") String param2,
                             @BindingCallback BridgeCallback callback) {
        RVLogger.d(TAG, "id: "+id+
                "\napp: "+app.toString()+
                "\npage: "+page.toString()+
                "\napiContext: "+apiContext.toString()+
                "\nexecutor: "+executor.toString());
        RVLogger.d(TAG, JSONUtils.toString(params));
        JSONObject result = BridgeResponse.SUCCESS.get();
        // result.put("message", "The client receives parameters:" + param1 + ", " + param2 + "\nThe current package name of the demo:" + apiContext.getActivity().getPackageName());
        // Return the result to the mini program.
        Stack stack = MriverApp.getAppStack();
        Enumeration enumerationLists = stack.elements();

        JSONArray jsonArray = new JSONArray();
        while (enumerationLists.hasMoreElements()) {
            JSONObject jsonObject = new JSONObject();
            MRApp o = (MRApp) enumerationLists.nextElement();
            jsonObject.put("AppId", o.getAppId());
            jsonObject.put("AppVersion", o.getAppVersion());
            jsonArray.add(jsonObject);
        }
        String tinyappStr = jsonArray.toJSONString();
        // result.put("message", "The client receives parameters:" + param1 + ", " + param2 + "\nThe current package name of the demo:" + apiContext.getActivity().getPackageName());
        result.put("message", tinyappStr);
        callback.sendJSONResponse(result);
    }

}

Enable the sharing feature

Mriver.setConfig("mr_showShareMenuItem", "YES");

// Implement ShareApiBridgeExtension
public class ShareApiBridgeExtension extends SimpleBridgeExtension {
    private static final String TAG = "CustomApiBridgeExtension";

    @ActionFilter
    public void shareTinyAppMsg(@BindingId String id,
                                @BindingNode(App.class) App app,
                                @BindingNode(Page.class) Page page,
                                @BindingApiContext ApiContext apiContext,
                                @BindingExecutor(ExecutorType.UI) Executor executor,
                                @BindingRequest JSONObject params,
                                final @BindingCallback BridgeCallback callback) {
        Log.i("ShareApiBridge", "share: " + (params == null ? "null" : params.toJSONString()));


        String title = params.getString("title");


        String desc = params.getString("desc");


        String myprop = params.getString("myprop");


        String path = params.getString("page");


        String appId = app.getAppId();




        // You can call the sharing component to implement subsequent features.


        String message = "Application ID: " + appId + "\n"


            + "title: " + title + "\n"


            + "desc: " + desc + "\n"


            + "myprop: " + myprop + "\n"


            + "path: " + path + "\n";




        AUNoticeDialog dialog = new AUNoticeDialog(apiContext.getActivity(),


            "Sharing result", message, "Sharing successful", "Sharing failed");


        dialog.setPositiveListener(new AUNoticeDialog.OnClickPositiveListener() {


            @Override


            public void onClick() {


                JSONObject result = BridgeResponse.SUCCESS.get();


                result.put("success", true);


                callback.sendJSONResponse(result);


            }


        });


        dialog.setNegativeListener(new AUNoticeDialog.OnClickNegativeListener() {


            @Override


            public void onClick() {

                callback.sendBridgeResponse(BridgeResponse.newError(11, "Sharing failed"));


            }


        });


        dialog.show();
    }
}

Permission pop-up window

// Customize the pop-up window for permission control alerts.
Mriver.setProxy(LocalPermissionDialogProxy.class, new LocalPermissionDialogProxy() {
                    @Override
                    public LocalPermissionDialog create(Context context) {
                        return new DemoLocalPermissionDialog(context);
                    }

                    @Override
                    public boolean interceptPermission(String appId, String page, String action, String scope, List<String> permissions) {
                        showToast("jsapi: " + action + "mini program: " + appId + "page: " + page);
                        return false;
                    }

                });
// Example of a pop-up window
public class DemoLocalPermissionDialog implements LocalPermissionMultiDialog {
    private Dialog mDialog;
    private final Context mContext;
    private PermissionPermitListener mPermissionPermitListener;

    public DemoLocalPermissionDialog(Context context) {
        this.mContext = context;
    }

	public void setExtData(String[] permissions, AppModel appModel, Page page, String action, String scope) {
  	 // Supports customization based on these parameters, including multi-level pop-up windows and custom copy styles.
     // permissions: the list of all permissions required by the current action.
     // appModel: the appModel of the current action, which can obtain the relevant copy defined by the mini program.
     // action: the action of the corresponding JSAPI.
     // scope: the scope of the JSAPI.
	}

    public void setDialogContent(String content, String title, String icon) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this.mContext);
        builder.setTitle("Permission pop-up window");
        builder.setMessage(content);
        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface pDialogInterface, int pI) {
                if (DemoLocalPermissionDialog.this.mPermissionPermitListener != null) {
                    DemoLocalPermissionDialog.this.mPermissionPermitListener.onSuccess();
                }
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface pDialogInterface, int pI) {
                if (DemoLocalPermissionDialog.this.mPermissionPermitListener != null) {
                    DemoLocalPermissionDialog.this.mPermissionPermitListener.onFailed(-1, "", true);
                }
            }
        });
        this.mDialog = builder.create();
        this.mDialog.show();
    }

    public void setPermissionPermitListener(PermissionPermitListener permissionPermitListener) {
        this.mPermissionPermitListener = permissionPermitListener;
    }

    public void show() {
        if (this.mDialog != null && this.mContext instanceof Activity && !((Activity)this.mContext).isFinishing()) {
            this.mDialog.show();
        }

    }

Note

Added more capabilities to the setExtData support permission pop-up window.

Custom view

  1. Upgrade appx to the 2.7.18 version.

    api ('com.mpaas.mriver:mriverappxplus-build:2.7.18.20220825001@aar') {
        force=true
    }
  2. Call related APIs.

    RVProxy.set(RVEmbedProxy.class, new RVEmbedProxy() {
    
                    @Override
                    public Class<?> getEmbedViewClass(String type) {
                        if ("custom_barrage".equalsIgnoreCase(type)) {
                            // The type must correspond to the type of the mini program. The corresponding custom view is returned based on the type.
                            return EmbedCustomView.class;
                        }
                        return null;
                    }
                });
  3. Implement a custom view.

    package com.mpaas.demo.tinyapp.engine;
    
    import android.content.Context;
    import android.text.TextUtils;
    import android.util.Log;
    import android.view.View;
    import android.widget.FrameLayout;
    
    import com.alibaba.ariver.app.api.Page;
    import com.alibaba.ariver.engine.api.bridge.extension.BridgeCallback;
    import com.alibaba.ariver.engine.api.bridge.extension.BridgeResponse;
    import com.alibaba.ariver.engine.api.embedview.IEmbedView;
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    import com.alipay.mobile.beehive.video.h5.live.MRLivePlayerHelper;
    import com.mpaas.mriver.integration.embed.IMREmbedView;
    
    import java.util.Map;
    
    public class EmbedCustomView implements IMREmbedView {
        private Context mContext;
        private Page mPage;
        private CustomBarrageView mCustomBarrageView; // The example of the pop-up view.
    
        @Override
        public void onCreate(Context context, Page page, IEmbedView iEmbedView) {
            mContext = context;
            mPage = page;
        }
    
        @Override
        public View getView(int width, int height, final String viewId, String type, Map<String, String> params) {
            Log.i("EmneCustomV", "getView: " + mCustomBarrageView + " " + viewId + " " + type + " " + params);
            if (mCustomBarrageView == null) {
                mCustomBarrageView = new CustomBarrageView(mContext);
            }
            FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height);
            mCustomBarrageView.setLayoutParams(layoutParams);
            return mCustomBarrageView;
        }
    
        // Receive the data sent by the mini program.
        @Override
        public void onReceivedMessage(String actionType, JSONObject data, BridgeCallback bridgeCallback) {
            Log.i("EmneCustomV", "onReceivedMessage: " +  actionType);
            if ("mpaasCustomEvent".equalsIgnoreCase(actionType)) {
                String innerAction = data.getString("actionType");
                if (TextUtils.equals(innerAction, "bindLivePlayer")) {
                    JSONObject dataJSON = data.getJSONObject("data");
    
                    // Set the barrage data.
                    JSONArray barrages = dataJSON.getJSONArray("barrages");
                    mCustomBarrageView.setData(barrages);
    
                    // Bind the liveplayer.
                    String bindId = dataJSON.getString("id");
                    if (!TextUtils.isEmpty(bindId)) {
                        MRLivePlayerHelper.bind(bindId, mCustomBarrageView);
                    }
                }
    
            }
        }
    
        protected void notifySuccess(final BridgeCallback bridgeContext) {
            if (bridgeContext != null) {
                bridgeContext.sendBridgeResponse(BridgeResponse.SUCCESS);
            }
        }
    
        
        @Override
        public void onReceivedRender(JSONObject params, BridgeCallback bridgeCallback) {
            Log.i("EmneCustomV", "onReceivedRender: " +  params);
            notifySuccess(bridgeCallback);
        }
    
        @Override
        public void onWebViewResume() {
    
        }
    
        @Override
        public void onWebViewPause() {
    
        }
    
        @Override
        public void onAttachedToWebView() {
    
        }
    
        @Override
        public void onDetachedToWebView() {
    
        }
    
        @Override
        public void onDestroy() {
    
        }
    
        @Override
        public void onRequestPermissionResult(int i, String[] strings, int[] ints) {
    
        }
    
        @Override
        public void onEmbedViewVisibilityChanged(int i) {
    
        }
    
        @Override
        public void initElementId(String s) {
    
        }
    
    }
    

Mini program-side implementation

//page.axml
<mpaas-component
          id="mpaas-barrage"
          type="custom_barrage" // The value of the type parameter. The value must be the same as the value of native.
          style="{{ width: 400, height: 200 }}" // You can only configure the width and height.
          onMpaasCustomEvent="onMpaasCustomEvent" // Receive native events
/>

//page.js

barrageContext = my.createMpaasComponentContext('mpaas-barrage');
// Send data to native.
barrageContext.mpaasCustomEvent({
        actionType: 'bindLivePlayer',
        data: {
          "id": "liveplayer",
          "barrages": ["Interesting", "Sofa", "Barrage 1", "Barrage 2", "Barrage 3"]
        }
      });
  }, 100)

Page lifecycle listener

  1. The following code is called during initialization.

    List<String> miniAppPoint = new ArrayList<>();
    miniAppPoint.add(PageResumePoint.class.getName());
    miniAppPoint.add(PagePausePoint.class.getName());
    miniAppPoint.add(PageEnterPoint.class.getName());
    miniAppPoint.add(AppExitPoint.class.getName());
    Mriver.registerPoint(PageLifeCycleExtension.class.getName(), miniAppPoint);
  2. Achieve PageLifeCycleExtension.java.

    public class PageLifeCycleExtension implements PageResumePoint, PageEnterPoint, PagePausePoint, AppExitPoint {
    
        private static final String TAG = "PageLifeCycleExtension";
    
        @Override
        public void onPageResume(Page page) {
        }
    
        @Override
        public void onInitialized() {
    
        }
    
        @Override
        public void onFinalized() {
    
        }
    
        @Override
        public void onPageEnter(Page page) {
    
        }
    
        @Override
        public void onPagePause(final Page page) {
        }
    
        @Override
        public void onAppExit(App app) {
            
        }
    }

APM listeners

Important

If the blank area of the page is large, the MiniPage_Load_T2 is not called back.

  1. Set the following content during initialization.

    RVProxy.set(PrepareNotifyProxy.class, new PrepareNotifyProxy() {
    
                            @Override
                            public void notify(String s, PrepareStatus prepareStatus) {
    
                            }
    
                            @Override
                            public void apmEvent(final String s, final String s1, final String s2, final String s3, final String s4) {
                              // Non-UI thread  
                              Log.i("MiniStartTime", "apmE: " + s + " " + s4);
                              if ("MiniAppStart".equalsIgnoreCase(s) || "MiniPage_Load_T2".equalsIgnoreCase(s)) {
                                  boolean isT2 = "MiniPage_Load_T2".equalsIgnoreCase(s);
                                  String apmData = s4;
                                  if (!TextUtils.isEmpty(apmData)) {
                                      parseTime(isT2, apmData);
                                  }
                              }
                          });
                    }
    
    
    
    
    private void parseTime(boolean isT2, String s4) {
            String[] kvArrs = s4.split("\\^");
            long miniStart = 0; // The time when the mini program is clicked.
            long miniPrepared = 0; // The time when the mini program preparation phase is completed. The first time the mini program is downloaded.
            long miniAppStarted = 0; // The time when the mini program core phase is completed.
            long miniT2 = 0; // The time when the T2 mini program is rendered.
            boolean needIgnore=false; // true indicates the second-level page rendering callback when the internal second-level page jumps. The first screen needs to be ignored.
    
            for (String kvItem : kvArrs) {
                String[] kv = kvItem.split("=");
                if (kv.length == 2) {
                    String key = kv[0];
                    String value = kv[1];
                    if ("mini_st_ts0".equalsIgnoreCase(key)) {
                        // The time when the mini program is clicked.
                        miniStart = Long.parseLong(value);
                    } else if ("mini_st_ts7".equalsIgnoreCase(key)) {
                        // The time when the mini program preparation phase is completed.
                        miniPrepared = Long.parseLong(value);
                    } else if ("mini_st_end_ts".equalsIgnoreCase(key)) {
                        // The time when the core phase of the mini program was completed.
                        miniAppStarted = Long.parseLong(value);
                    } else if ("mini_t2_ts".equalsIgnoreCase(key)) {
                        // The time when the T2 mini program is rendered.
                        miniT2 = Long.parseLong(value);
                    } else if (isT2 && "isFirstPage".equalsIgnoreCase(key)) {
                        if ("false".equalsIgnoreCase(value)) {
                            needIgnore = true;
                        }
                    }
                }
            }
    
            if (!needIgnore && miniStart > 0 && miniPrepared > 0 && miniAppStarted > 0 && miniT2 > 0) {
                final String toastStr = "Preparation time=" + (miniPrepared-miniStart) + "Core time=" + (miniAppStarted-miniPrepared) + "Business time=" + (miniT2-miniAppStarted) + "Total time=" + (miniT2-miniStart);
                Log.i("MiniStartTime", toastStr);
                mUIHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MRiverApp.sApp, toastStr, Toast.LENGTH_LONG).show();
                    }
                });
            }
        }
  2. Add the timestamp parameter when you start the mini program.

    Bundle intent  = new Bundle();
    intent.putString("miniapp_start_ts", Long.toString(System.currentTimeMillis()));
    Mriver.startApp(FastStartActivity.this, “appId”, intent); 

Support for Google Maps

  1. Turn on the switch to switch to Google Maps.

    Mriver.setConfig("ta_map_type", "1");
  2. Add the Google Maps dependency and configure the key for Google Maps.