Что является альтернативой для "android: используетCleartextTraffi c" в android API LEVEL 29? - PullRequest
0 голосов
/ 04 мая 2020

Я разрабатываю приложение Wordpress android, которое загружает данные постов Wordpress в android веб-просмотре. Я использую retrofit & JSON API для получения данных с моего сайта Wordpress. Мое приложение работало нормально в android API LEVEL 26, но проблема в том, что мне нужен API 28+ для загрузки в магазин Google Play. Я мигрировал на androidx API LEVEL 29. сообщение не загружалось, показывая ERR_CLEARTEXT_NOT_PERMITTED, поэтому я использовал "android:usesCleartextTraffic=true" сообщение загружалось, но проблема в том, что HTML сущностям типа &#8211,&#8220,&#8217 et c не удалось декодировать и декодировать как & и остальные строки поста не загружаются после этого. Я не использую "android:usesCleartextTraffic=true" в API LEVEL 26, но он работает без ошибок. Я ищу решение своей проблемы, но нигде не нашел удовлетворительных ответов. Я застрял в этой точке. Поэтому я пробую альтернативный метод для "android:usesCleartextTraffic=true". У меня есть функция "текст в речь" в моем приложении, оно работает абсолютно нормально, я имею в виду его чтение всего поста, который не виден в android веб-просмотре. Пожалуйста, предложите. Пожалуйста, найдите код для вашей справки.

SCREESHOT WEBVIEW

Это моя постдеталийская деятельность

Постдеталий. java

package ak.wp.meto.activity;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.fragment.app.FragmentManager;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.text.Html;
import android.view.MenuItem;
import android.view.View;
import android.webkit.WebView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import ak.wp.meto.R;
import ak.wp.meto.adapters.CommentsAdapter;
import ak.wp.meto.api.http.ApiUtils;
import ak.wp.meto.api.models.posts.post.CommentsAndReplies;
import ak.wp.meto.api.models.posts.post.PostDetails;
import ak.wp.meto.api.params.HttpParams;
import ak.wp.meto.data.constant.AppConstant;
import ak.wp.meto.data.sqlite.FavouriteDbController;
import ak.wp.meto.fragment.WriteACommentFragment;
import ak.wp.meto.listeners.ListItemClickListener;
import ak.wp.meto.models.FavouriteModel;
import ak.wp.meto.utility.ActivityUtils;
import ak.wp.meto.utility.AppUtils;
import ak.wp.meto.utility.TtsEngine;
import ak.wp.meto.webengine.WebEngine;
import ak.wp.meto.webengine.WebListener;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class PostDetailsActivity extends BaseActivity implements WriteACommentFragment.OnCompleteListener {

    // Variables
    private Activity mActivity;
    private Context mContext;

    // init views
    private RelativeLayout lytPostDetailsView, lytCommentDetails;
    private int clickedPostId;
    private ImageView imgPost;
    private TextView tvPostTitle, tvPostAuthor, tvPostDate, tvCommnentList;
    private WebView webView;
    private FloatingActionButton fabWriteAComment;
    private ImageButton imgBtnSpeaker, imgBtnFav, imgBtnShare;
    private PostDetails model = null;

    // Favourites view
    private List<FavouriteModel> favouriteList;
    private FavouriteDbController favouriteDbController;
    private boolean isFavourite = false;

    // Comments view
    private List<CommentsAndReplies> commentList;
    private List<CommentsAndReplies> zeroParentComments;
    List<CommentsAndReplies> onlyThreeComments;
    private int mPerPage = 5;
    private RecyclerView rvComments;
    private CommentsAdapter commentsAdapter = null;

    // Text to speech
    private TtsEngine ttsEngine;
    private boolean isTtsPlaying = false;
    private String ttsText;

    private WebEngine webEngine;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        initVar();
        initView();
        initFunctionality();
        initListener();
    }

    private void initVar() {
        mActivity = PostDetailsActivity.this;
        mContext = mActivity.getApplicationContext();

        // Favourites view
        favouriteList = new ArrayList<>();

        // Comments view
        commentList = new ArrayList<>();
        zeroParentComments = new ArrayList<>();
        onlyThreeComments = new ArrayList<>();

        Intent intent = getIntent();
        if (intent != null) {
            clickedPostId = getIntent().getIntExtra(AppConstant.BUNDLE_KEY_POST_ID, 0);
        }
    }

    private void initView() {
        setContentView(R.layout.activity_post_details);

        lytPostDetailsView = (RelativeLayout) findViewById(R.id.lyt_post_details);
        lytCommentDetails = (RelativeLayout) findViewById(R.id.lyt_comment_list);
        //lytParentView.setVisibility(View.GONE);

        imgPost = (ImageView) findViewById(R.id.post_img);
        tvPostTitle = (TextView) findViewById(R.id.title_text);
        tvPostAuthor = (TextView) findViewById(R.id.post_author);
        tvPostDate = (TextView) findViewById(R.id.date_text);
        imgBtnSpeaker = (ImageButton) findViewById(R.id.imgBtnSpeaker);
        imgBtnFav = (ImageButton) findViewById(R.id.imgBtnFavourite);
        imgBtnShare = (ImageButton) findViewById(R.id.imgBtnShare);

        initWebEngine();


        tvCommnentList = (TextView) findViewById(R.id.comment_count);
        fabWriteAComment = (FloatingActionButton) findViewById(R.id.fab_new_comment);

        rvComments = (RecyclerView) findViewById(R.id.rvComments);
        rvComments.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

        initLoader();

        initToolbar();
        enableBackButton();

    }

    public void initWebEngine() {

        webView = (WebView) findViewById(R.id.web_view);
        webEngine = new WebEngine(webView, mActivity);
        webEngine.initWebView();
        webEngine.initListeners(new WebListener() {
            @Override
            public void onStart() {
                initLoader();
            }

            @Override
            public void onLoaded() {
                hideLoader();
            }

            @Override
            public void onProgress(int progress) {
            }

            @Override
            public void onNetworkError() {
                showEmptyView();
            }

            @Override
            public void onPageTitle(String title) {
            }
        });
    }

    private void initFunctionality() {

        favouriteDbController = new FavouriteDbController(mContext);
        favouriteList.addAll(favouriteDbController.getAllData());
        for (int i = 0; i < favouriteList.size(); i++) {
            if (favouriteList.get(i).getPostId() == clickedPostId) {
                isFavourite = true;
                break;
            }
        }

        ttsEngine = new TtsEngine(mActivity);

        commentsAdapter = new CommentsAdapter(mActivity, (ArrayList) commentList, (ArrayList) onlyThreeComments);
        rvComments.setAdapter(commentsAdapter);

        showLoader();
        loadPostDetails();

        // show full-screen ads
        // AdUtils.getInstance(mContext).showFullScreenAd();

    }

    public void setFavImage() {
        if (isFavourite) {
            imgBtnFav.setImageDrawable(ContextCompat.getDrawable(mActivity, R.drawable.ic_book));
        } else {
            imgBtnFav.setImageDrawable(ContextCompat.getDrawable(mActivity, R.drawable.ic_un_book));
        }
    }

    public void initListener() {

        imgBtnSpeaker.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (model != null) {
                    toggleTtsPlay();
                }
            }
        });

        imgBtnFav.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (model != null) {
                    isFavourite = !isFavourite;
                    if (isFavourite) {
                        String imgUrl = null;
                        if (model.getEmbedded().getWpFeaturedMedias().size() >= 1) {
                            imgUrl = model.getEmbedded().getWpFeaturedMedias().get(0).getMediaDetails().getSizes().getFullSize().getSourceUrl();
                        }
                        favouriteDbController.insertData(
                                model.getID().intValue(),
                                imgUrl,
                                model.getTitle().getRendered(),
                                model.getOldDate(),
                                model.getEmbedded().getWpTerms().get(0).get(0).getName()
                        );
                    } else {
                        favouriteDbController.deleteFav(clickedPostId);
                    }
                    setFavImage();
                }
            }
        });

        imgBtnShare.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (model != null) {
                    Intent sendIntent = new Intent();
                    sendIntent.setAction(Intent.ACTION_SEND);
                    sendIntent.putExtra(Intent.EXTRA_TEXT, model.getPageUrl());
                    sendIntent.setType("text/plain");
                    startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to)));
                }
            }
        });

        commentsAdapter.setItemClickListener(new ListItemClickListener() {
            @Override
            public void onItemClick(int position, View view) {
                int id = view.getId();
                CommentsAndReplies clickedComment = zeroParentComments.get(position);
                switch (id) {
                    case R.id.list_item:
                        ActivityUtils.getInstance().invokeCommentDetails(mActivity, CommentDetailsActivity.class, (ArrayList) commentList, clickedPostId, clickedComment, false, false);
                        break;
                    case R.id.reply_text:
                        ActivityUtils.getInstance().invokeCommentDetails(mActivity, CommentDetailsActivity.class, (ArrayList) commentList, clickedPostId, clickedComment, true, false);
                        break;
                    default:
                        break;
                }
            }
        });

        tvCommnentList.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ActivityUtils.getInstance().invokeCommentList(mActivity,
                        CommentListActivity.class,
                        (ArrayList) commentList,
                        (ArrayList) zeroParentComments,
                        clickedPostId,
                        false);
            }
        });

        fabWriteAComment.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentManager manager = getSupportFragmentManager();
                WriteACommentFragment dialog = WriteACommentFragment.newInstance(clickedPostId, AppConstant.THIS_IS_COMMENT);
                dialog.show(manager, AppConstant.BUNDLE_KEY_DIALOG_FRAGMENT);
            }
        });
    }

    public void loadPostDetails() {
        ApiUtils.getApiInterface().getPostDetails(clickedPostId).enqueue(new Callback<PostDetails>() {
            @Override
            public void onResponse(Call<PostDetails> call, Response<PostDetails> response) {
                if (response.isSuccessful()) {
                    // bind data
                    model = response.body();
                    PostDetails m = model;
                    // visible parent view
                    lytPostDetailsView.setVisibility(View.VISIBLE);
                    // visible comments button
                    fabWriteAComment.setVisibility(View.VISIBLE);

                    loadCommentsAndReplies(model.getLinks().getRepliesList().get(0).getHref());

                    setFavImage();


                    tvPostTitle.setText(Html.fromHtml(model.getTitle().getRendered()));

                    String imgUrl = null;
                    if (model.getEmbedded().getWpFeaturedMedias().size() > 0) {
                        if (model.getEmbedded().getWpFeaturedMedias().get(0).getMediaDetails() != null) {
                            if (model.getEmbedded().getWpFeaturedMedias().get(0).getMediaDetails().getSizes().getFullSize().getSourceUrl() != null) {
                                imgUrl = model.getEmbedded().getWpFeaturedMedias().get(0).getMediaDetails().getSizes().getFullSize().getSourceUrl();
                            }
                        }
                    }

                    if (imgUrl != null) {
                        Glide.with(getApplicationContext())
                                .load(imgUrl)
                                .into(imgPost);
                    }

                    String author = null;
                    if (model.getEmbedded().getAuthors().size() >= 1) {
                        author = model.getEmbedded().getAuthors().get(0).getName();
                    }

                    if (author == null) {
                        author = getString(R.string.admin);
                    }
                    tvPostAuthor.setText(Html.fromHtml(author));

                    String oldDate = model.getOldDate();
                    String newDate = AppUtils.getFormattedDate(oldDate);

                    if (newDate != null) {
                        tvPostDate.setText(Html.fromHtml(newDate));
                    }

                    String contentText = model.getContent().getRendered();

                    ttsText = new StringBuilder(Html.fromHtml(model.getTitle().getRendered())).append(AppConstant.DOT).append(Html.fromHtml(model.getContent().getRendered())).toString();
                    webView.loadData(contentText, "text/html", "UTF-8"); //comment
                    contentText = new StringBuilder().append(AppConstant.CSS_PROPERTIES).append(contentText).toString();
                    webEngine.loadHtml(contentText);

                } else {
                    showEmptyView();
                }
            }

            @Override
            public void onFailure(Call<PostDetails> call, Throwable t) {
                t.printStackTrace();
                // hide common loader
                hideLoader();
                // show empty view
                showEmptyView();
            }
        });
    }

    public void loadCommentsAndReplies(final String commentsAndRepliesLink) {

        ApiUtils.getApiInterface().getCommentsAndReplies(commentsAndRepliesLink, mPerPage).enqueue(new Callback<List<CommentsAndReplies>>() {
            @Override
            public void onResponse(Call<List<CommentsAndReplies>> call, Response<List<CommentsAndReplies>> response) {
                if (response.isSuccessful()) {

                    int totalItems = Integer.parseInt(response.headers().get(HttpParams.HEADER_TOTAL_ITEM));
                    int totalPages = Integer.parseInt(response.headers().get(HttpParams.HEADER_TOTAL_PAGE));


                    if (totalPages > 1) {
                        mPerPage = mPerPage * totalPages;
                        loadCommentsAndReplies(commentsAndRepliesLink);

                    } else {
                        if (!commentList.isEmpty() || !zeroParentComments.isEmpty() || !onlyThreeComments.isEmpty()) {
                            commentList.clear();
                            zeroParentComments.clear();
                            onlyThreeComments.clear();
                        }

                        commentList.addAll(response.body());

                        lytCommentDetails.setVisibility(View.VISIBLE);

                        if (commentList.size() > 0) {
                            for (CommentsAndReplies commentsAndReplies : commentList) {
                                if (commentsAndReplies.getParent().intValue() == 0) {
                                    zeroParentComments.add(commentsAndReplies);
                                }
                            }

                            if (zeroParentComments.size() >= 3) {
                                for (int i = 0; i < 3; i++) {
                                    onlyThreeComments.add(zeroParentComments.get(i));
                                }
                            } else {
                                for (CommentsAndReplies commentsAndReplies : zeroParentComments) {
                                    onlyThreeComments.add(commentsAndReplies);
                                }
                            }
                            commentsAdapter.notifyDataSetChanged();
                            tvCommnentList.setText(String.format(getString(R.string.all_comment), commentList.size()));
                            tvCommnentList.setClickable(true);

                        } else {
                            tvCommnentList.setClickable(false);
                        }
                    }

                }
            }

            @Override
            public void onFailure(Call<List<CommentsAndReplies>> call, Throwable t) {
                showEmptyView();
                t.printStackTrace();
            }
        });
    }

    private void toggleTtsPlay() {
        if (isTtsPlaying) {
            ttsEngine.releaseEngine();
            isTtsPlaying = false;
        } else {
            ttsEngine.startEngine(ttsText);
            isTtsPlaying = true;
        }
        toggleTtsView();
    }

    private void toggleTtsView() {
        if (isTtsPlaying) {
            imgBtnSpeaker.setImageDrawable(getResources().getDrawable(R.drawable.ic_speaker_stop));
        } else {
            imgBtnSpeaker.setImageDrawable(getResources().getDrawable(R.drawable.ic_speaker));
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            // Respond to the action bar's Up/Home button
            case android.R.id.home:
                finish();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        finish();
    }

    @Override
    protected void onStop() {
        super.onStop();
        ttsEngine.releaseEngine();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ttsEngine.releaseEngine();
        model = null;
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (isTtsPlaying) {
            isTtsPlaying = false;
            imgBtnSpeaker.setImageDrawable(getResources().getDrawable(R.drawable.ic_speaker));
        }
    }

    @Override
    public void onComplete(Boolean isCommentSuccessful, CommentsAndReplies commentsAndReplies) {
        if (isCommentSuccessful) {
            loadCommentsAndReplies(model.getLinks().getRepliesList().get(0).getHref());
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != RESULT_OK) {
            return;
        }

        if (requestCode == AppConstant.REQUEST_CODE_COMMENT) {
            if (data == null) {
                return;
            }
            boolean isCommentSuccessful = CommentListActivity.wasCommentSuccessful(data);
            if (isCommentSuccessful) {
                loadCommentsAndReplies(model.getLinks().getRepliesList().get(0).getHref());
            }
        }
    }
}

Activity_post_details. xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:fitsSystemWindows="true">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:expandedTitleMarginEnd="64dp"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/post_img"
                android:layout_width="match_parent"
                android:layout_height="@dimen/margin_250dp"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                android:src="@color/white"
                app:layout_collapseMode="parallax" />

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                    <ImageButton
                        android:id="@+id/imgBtnSpeaker"
                        android:layout_width="30dp"
                        android:layout_height="30dp"
                        android:layout_centerVertical="true"
                        android:layout_marginRight="10dp"
                        android:layout_toLeftOf="@id/imgBtnFavourite"
                        android:padding="5dp"
                        android:scaleType="centerInside"
                        android:src="@drawable/ic_speaker" />
                </RelativeLayout>

            </androidx.appcompat.widget.Toolbar>
        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <include layout="@layout/content_post_details" />
            <include layout="@layout/content_comments" />
        </LinearLayout>

    </androidx.core.widget.NestedScrollView>
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_new_comment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/activity_vertical_margin"
        android:src="@drawable/ic_fab"
        android:visibility="gone" />
    <include layout="@layout/view_common_loader" />    </androidx.coordinatorlayout.widget.CoordinatorLayout>

content_post_details. xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/lyt_post_details"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:visibility="gone">

    <LinearLayout
        android:id="@+id/li_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/margin_8dp"
        android:layout_marginTop="@dimen/margin_8dp"
        android:orientation="horizontal"
        android:weightSum="1">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/margin_20dp"
            android:layout_weight="0.5"
            android:gravity="center_vertical"
            android:padding="@dimen/margin_8dp">

            <ImageView
                android:id="@+id/author_image"
                android:layout_width="@dimen/margin_30dp"
                android:layout_height="@dimen/margin_30dp"
                android:background="@drawable/ic_author" />    
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/margin_20dp"
            android:layout_weight="0.5"
            android:gravity="center_vertical"
            android:padding="@dimen/margin_8dp">
        </LinearLayout>

    </LinearLayout>

    <View
        android:id="@+id/viewDivider"
        android:layout_width="match_parent"
        android:layout_height="0.7dp"
        android:layout_below="@id/li_layout"
        android:layout_marginLeft="@dimen/margin_15dp"
        android:layout_marginRight="@dimen/margin_15dp"
        android:background="@color/toolbar_boarder" />

    <TextView
        android:id="@+id/title_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/viewDivider"
        android:layout_margin="@dimen/margin_8dp"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:textStyle="bold"
        tools:text="This is sample text do yoy know that it supports multi-line" />

    <WebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/title_text"
        android:layout_margin="@dimen/margin_8dp"
        android:paddingBottom="@dimen/margin_8dp" />

</RelativeLayout>
...