In my Application class I have this method, that I call on the onCreate of my Application:
public void initInstabug() {
try {
instabug = new Instabug.Builder(this, "45c752889689ac2ae0840d11e2a5f628")
.setDebugEnabled(true)
.setEmailFieldRequired(true)
.setFloatingButtonOffsetFromTop(400)
.setShouldShowIntroDialog(true)
.setColorTheme(IBGColorTheme.IBGColorThemeLight)
.setCommentFieldRequired(true)
.setInvocationEvent(IBGInvocationEvent.IBGInvocationEventShake)
.build();
instabug.setPrimaryColor(getResources().getColor(R.color.background));
instabug.setPreSendingRunnable(new Runnable() {
#Override
public void run() {
Log.i("","instabug entered pre sending runnable");
String[] files = new String[2];
files[0] = Environment.getExternalStorageDirectory() + "/Passenger/passenger_log.txt";
files[1] = Environment.getExternalStorageDirectory() + "/Passenger/passenger_log2.txt";
Compress compress = new Compress(files, Environment.getExternalStorageDirectory() + "/Passenger/log.zip");
compress.zip(new CrudStateCallback() {
#Override
public void onResponse(String string) {
Log.i("", "instabug ended making the archive");
}
});
}
});
File file = new File(Environment.getExternalStorageDirectory() + "/Passenger/log.zip");
Uri uri = Uri.fromFile(file);
instabug.setFileAttachment(uri, null);
}catch (Exception e){
Log.e("","error instabug:" + e.getMessage());
Utils.appendLog("In case instabug crashes asyncTask process, will not crash app",true);
}
}
My Issue is that I have the login page, first, and the popup always comes up while I ussually write my login info (email, password).
Is it possible to delay the introDialog, so it will appear only when another activity is open?
I tried initialising my instabug instance with this code in Application, where .setShouldShowIntroDialog(FALSE) is set to false, as you can see. And then in the timeline I reinit it with .setShouldShowIntroDialog(true). But it doesn't show at all like this.
You can try to use Instabug.showIntroMessage() in your activity and set the .setShouldShowIntroDialog() to false.
Related
I am trying to log the exception plus the user navigation for x amount of time. For example, when my application got an exception I will append it in a text file. Now from that point of time of time, I need to log only for a certain time. e.g., 1 hour. Is it possible to do it? This is the code I wrote to get the exception information and log it in a file.
Please someone help me with this. Thanks in advance.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(handleAppCrash);
}
private Thread.UncaughtExceptionHandler handleAppCrash =
new Thread.UncaughtExceptionHandler() {
#SuppressLint("LongLogTag")
#Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!file.exists()) {
file.mkdir();
}
try {
data = android_version + "#" + Device + "#" + username + "#" + version + "#" + dates + "#" + Logtrace;
File gpxfile = new File(file, fname);
FileWriter writer = new FileWriter(gpxfile,true);
writer.append(data);
writer.flush();
writer.close();
} catch (
Exception e) {
e.printStackTrace();
}
}
};
You are looking for a way to
restart the app
write more logs to the same file that you created in the UncaughtExceptionHandler.
Firstly, to restart the app, you can take the following steps:
Created a pending intent, e.g., in your onCreate (where Intent intent has class scope, already defined, i.e., not just defined within onCreate):
intent = PendingIntent.getActivity(
YourApplication.getInstance().getBaseContext(),
0,
new Intent(getIntent()),
getIntent().getFlags());
After your try/catch in your UncaughtExceptionHandler, start an alarm to trigger your app in some amount of time, e.g., 1 second; and you must follow this with a System.exit();. This is so the current dying app will properly quit, so that in 1 second, when the alarm triggers, it will start the app again (but it won't if the app is still running).
AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, intent);
System.exit(2);
You could optionally store the name of the file (fname in your code) to SharedPreferences (for writing to the same file when the app restarts). Or it could be a fixed hardcoded name that your app knows, and doesn't need to save the name.
You could saved a Boolean in SharedPreferences to let your app know that you are restarting from an uncaught exception.
Secondly, whenever the app starts:
Check the Boolean in SharedPreferences; if it is a normal start, proceed like normal.
If it is a restart after uncaught exception, then retrieve the file name from SharedPreferences (or get it hard coded), then you can write more logs to the file. As in Android, a file is uniquely determined by path and file name. With the same file name, you can open the same file.
Define a background service
To do anything in the background for a prolonged time you should use a service. I'm using a JobService in this example.
<service android:name="org.example.LogService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE"
android:process=":logprocess"/>
Note the process tag, it is important, because you'll want to kill your current process after an uncaught exception.
public class LogService extends JobService {
private Thread thread;
#Override
public boolean onStartJob(final JobParameters params) {
if (!file.exists()) {
file.mkdir();
}
thread = new Thread(() -> {
try {
data = params.getExtras().getString("data");
File gpxfile = new File(file, fname);
FileWriter writer = new FileWriter(gpxfile,true);
writer.append(data);
//continue to write your logs to the file here for as long as you want. you could copy logcat to the file for example.
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
jobFinished(params, false);
});
thread.start();
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
thread.interrupt();
return true;
}
}
Call your background service
public void uncaughtException(Thread thread, Throwable ex) {
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
PersistableBundle extras = new PersistableBundle();
extras.putString("data", android_version + "#" + Device + "#" + username + "#" + version + "#" + dates + "#" + Logtrace);
JobInfo.Builder builder = new JobInfo.Builder(0, new ComponentName(context, LogService.class))
builder.setExtras(extras);
builder.setOverrideDeadline(1000);
scheduler.schedule(builder.build());
//stop the current process.
Process.killProcess(Process.myPid());
System.exit(10);
}
Killing the current process is optional but recommended, the application could be in a bad state after an uncaught exception. If you want to restart your application you can call startActivity from the LogService.
Note:
You've not given any details on what exactly you want to log for x amount of time, so this code only has a comment where the log collection goes.
Note 2:
This code was adapted from the ACRA project of which I am a maintainer, specifically manifest JobSenderService DefaultSenderScheduler and ProcessFinisher
I'm trying to display a PDF on Android in a Xamarin.Forms project and it works fine, except for the first time it's loaded where just one blank page appears 9 times out of 10.
The first call is to this function, located in the Android project:
public string HTMLToPDF(string html, string filename) {
//html param is a full html description of the pdf
//filename param is something like "example.pdf"
try {
var dir = new Java.IO.File(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + "/folder/");
var file = new Java.IO.File(dir + "/" + filename);
if (!dir.Exists())
dir.Mkdirs();
int x = 0;
while (file.Exists())
{
x++;
file = new Java.IO.File(dir + "/" + filename + "( " + x + " )");
}
if (webpage == null)
webpage = new Android.Webkit.WebView(Android.App.Application.Context);
else
webpage.RemoveAllViews();
int width = 2100;
int height = 2970;
webpage.Layout(0, 0, width, height);
webpage.LoadData(html, "text/html", "UTF-8");
webpage.SetWebViewClient(new WebViewCallBack(file.ToString()));
this.Print(webpage, file.ToString(), filename);
return file.ToString();
}
catch (Java.Lang.Exception e)
{
App._mainPage.DisplayAlert("Error", e.Message, "Ok");
}
return "";
}
This is what the WebViewCallBack class looks like:
class WebViewCallBack : Android.Webkit.WebViewClient
{
string fileNameWithPath = null;
public WebViewCallBack(string path)
{
this.fileNameWithPath = path;
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
view.SetInitialScale(1);
view.Settings.LoadWithOverviewMode = true;
view.Settings.UseWideViewPort = true;
PdfDocument document = new Android.Graphics.Pdf.PdfDocument();
Android.Graphics.Pdf.PdfDocument.Page page = document.StartPage(new Android.Graphics.Pdf.PdfDocument.PageInfo.Builder(2100, 2970, 1).Create());
view.Draw(page.Canvas);
document.FinishPage(page);
Stream filestream = new MemoryStream();
Java.IO.FileOutputStream fos = new Java.IO.FileOutputStream(fileNameWithPath, false);
try
{
document.WriteTo(filestream);
fos.Write(((MemoryStream)filestream).ToArray(), 0, (int)filestream.Length);
fos.Close();
}
catch (Java.Lang.Exception e)
{
App._mainPage.DisplayAlert("Erreur", e.Message, "Ok");
}
}
}
And the method Print called at the end:
public void Print(Android.Webkit.WebView webView, string filename, string onlyFileName)
{
try
{
PrintAttributes.Builder builder = new PrintAttributes.Builder();
PrintAttributes.Margins margins = new PrintAttributes.Margins(0, 0, 0, 80);
builder.SetMinMargins(margins);
builder.SetMediaSize(PrintAttributes.MediaSize.IsoA4);
builder.SetColorMode(PrintColorMode.Color);
PrintAttributes attr = builder.Build();
PrintManager printManager = (PrintManager)Forms.Context.GetSystemService(Android.Content.Context.PrintService);
var printAdapter = new GenericPrintAdapter(Forms.Context, webView, filename, onlyFileName);
printAdapter.OnEnded += PrintAdapter_OnEnded;
printAdapter.OnError += PrintAdapter_OnError;
printManager.Print(filename, printAdapter, attr);
}
catch (Java.Lang.Exception e)
{
App._mainPage.DisplayAlert("Erreur", e.Message, "Ok");
}
}
When I put a breakpoint on the first line of the OnPageFinished callback of the WebViewCallBack class, I see two different things at this point:
either the PDF interface is up but it's still loading the PDF. In that case, the PDF loads fine once I click on "play" again.
either the PDF has already loaded, as a single blank page. This only happens when it's the first try at loading this particular PDF.
Thus I guess I have to find a way to force the loader to wait for the OnPageFinished method to run first? But that seems wrong.
I can also add that the original HTML contains images, which are all appearing as base64 string in the html string I'm feeding to HTMLToPDF. I noticed that the PDF loads well even on the first try if there are no images in the HTML, so I thought the problem might be that the PDF loads before it's ready on the first try only, maybe because of the images. I couldn't find a fix for that though.
Can anybody shed some light on this for me?
So there are a couple of ways to handle the flow, but I would go with just an event or an Observable.
So lets convert your WebViewCallBack to something that actually performs a callback when its OnPageFinished is called. This is using System.Reactive but you could also use an EventHandler...
// a few class level variables
bool busy;
WebView webView;
IDisposable WhenPageIsLoadedSubscription;
public class WebViewObservable : WebViewClient
{
Subject<string> pageLoaded = new Subject<string>();
public IObservable<string> WhenPageIsLoaded
{
get { return pageLoaded.AsObservable(); }
}
public override void OnPageFinished(WebView view, string url)
{
pageLoaded.OnNext(url);
}
}
Now define your print routine (the calls in your original OnPageFinished and Print method). This will automatically be called when the webpage is finished loading.
void PrintWebPage(WebView webView, string url)
{
Log.Debug("SO", $"Page: {url} loaded, lets print it now" );
// Perform the work that you used to do in OnPageFinished
// Perform the work in your Print method
// Now turn off a Progress indictor if desired
busy = false;
}
Setup your WebView with the observable that calls the PrintWebPage action every time a page is loaded...
void LoadAndPrintWebPage(string html))
{
busy = true;
if (webView == null)
{
int width = 2100;
int height = 2970;
webView = new WebView(this);
var client = new WebViewObservable();
WhenPageIsLoadedSubscription = client.WhenPageIsLoaded.Subscribe((url) => { PrintWebPage(webView, url); });
webView.SetWebViewClient(client);
webView.Layout(0, 0, width, height);
}
webView.LoadData(html, "text/html", "UTF-8");
}
Now call LoadWebPage with your html content and it will be automatically printed after the page is finished loading...
LoadAndPrintWebPage(html);
When you are done, clean up your observable and webview to avoid memory leaks...
void CleanupWebView()
{
WhenPageIsLoadedSubscription?.Dispose();
webView?.Dispose();
webView = null;
}
I had similar problem with blank page. But I used google doc for displaying it. http://docs.google.com/gview?embedded=true&url=
I could solved my problem only by checking operation time between OnPageStarted and OnPageFinished. If it took short time, then something go wrong and page is blank.
here is my webClient.
Every time then page is blank I just reload it. Also add simple circuit breaker just in case
public class MyWebViewClient : WebViewClient
{
private readonly WebView _webView;
private DateTime _dateTime = DateTime.Now;
private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1);
private int _breakerHits;
private const int _BreakerCount = 2;
public MyWebViewClient(WebView webView)
{
_webView = webView;
}
public override async void OnPageStarted(Android.Webkit.WebView view, string url, Bitmap favicon)
{
await _semaphoreSlim.WaitAsync();
_dateTime = DateTime.Now;
base.OnPageStarted(view, url, favicon);
_webView.SendNavigating(new WebNavigatingEventArgs(WebNavigationEvent.NewPage, null, url));
_semaphoreSlim.Release();
}
public override async void OnPageFinished(Android.Webkit.WebView view, string url)
{
await _semaphoreSlim.WaitAsync();
base.OnPageFinished(view, url);
if (url.Contains(".pdf"))
{
var diff = DateTime.Now - _dateTime;
if (diff > TimeSpan.FromMilliseconds(700) || _breakerHits > _BreakerCount)
{
_breakerHits = 0;
_webView.SendNavigated(new WebNavigatedEventArgs(WebNavigationEvent.NewPage, null, url,
WebNavigationResult.Success));
}
else
{
_breakerHits++;
view.Reload();
}
}
else
{
_webView.SendNavigated(new WebNavigatedEventArgs(WebNavigationEvent.NewPage, null, url,
WebNavigationResult.Success));
}
_semaphoreSlim.Release();
}
}
How can I make an AsyncTask's doInBackground() only return when another async method completes?
On a high level, the Activity's purpose is to allow the user to upload a review. The review may also have images. Now in my code, I have two AsyncTasks: ImageCompressionTask and ImageUploadTask. Basically, once the user selects images to upload, ImageCompressionTask is executed for each image. In the onPostExecute() method of that task, ImageUploadTask is executed. The purpose of ImageUploadTask is to upload the compressed image and update the Firestore (database) with the download URL of the aforementioned uploaded image. Following is its code:
public class ImageUploadTask extends AsyncTask<byte[], Integer, Void> {
#Override
protected Void doInBackground(byte[]... bytes) {
StorageReference ref = App.getFireStorage().getReference();
ref = ref.child("review_images/" +
mDestinationId + "/" +
mExistingReview.getReviewId() + "/" +
mUploadProgressCount);
ref.putBytes(bytes[0]).addOnSuccessListener(taskSnapshot -> {
String url = taskSnapshot.getDownloadUrl().toString();
mExistingReview.getImages().add(url);
App.getFirestore().collection("reviews").document(mExistingReview.getReviewId()).set(mExistingReview);
mUploadProgressCount++;
});
return null;
}
}
However, the issue is that the doInBackground() returns prematurely. I understand WHY it happens (because the ref.putBytes() method runs asynchronously) but I want it to wait until ref.putBytes() finishes. How can I do that?
Can you please check it this way :
#Override
protected synchronized Void doInBackground(byte[]... bytes) {
StorageReference ref = App.getFireStorage().getReference();
ref = ref.child("review_images/" +
mDestinationId + "/" +
mExistingReview.getReviewId() + "/" +
mUploadProgressCount);
ref.putBytes(bytes[0]).addOnSuccessListener(taskSnapshot -> {
String url = taskSnapshot.getDownloadUrl().toString();
mExistingReview.getImages().add(url);
App.getFirestore().collection("reviews").document(mExistingReview.getReviewId()).set(mExistingReview);
mUploadProgressCount++;
});
return null;
}
i have solved this problem with ObservableInteger
is a listener that listen for values
private ObservableInteger mObsInt;
//Listener
mObsInt = new ObservableInteger();
mObsInt.set(0);
mObsInt.setOnIntegerChangeListener(new OnIntegerChangeListener()
{
#Override
public void onIntegerChanged(int newValue)
{
if (mObsInt.get()==1)
Log.e("Downloads"," mObsInt 1");
Log.e("Download1"," Finished first process ");
if (mObsInt.get()==2){
Log.e("Downloads"," mObsInt 2");
Log.e("Download2"," Finished second process ");
mProgressDialog.dismiss();
Intent mainIntent = new Intent().setClass(LoginActivity.this, Principal.class);
startActivity(mainIntent);
finish();
}
}
});
and then just do this (after a process has finished or an asynctask)
mObsInt.set(mObsInt.get()+1);
so it will count, if the first thing finish obsInt will be 1 , and when the second one finish, obsInt will be 2, so after obsInt == 2 , you can move on to the other activity or process you need
happy coding !
This can be accomplished with CountDownLatch:
public class ImageUploadTask extends AsyncTask<byte[], Integer, Void> {
#Override
protected Void doInBackground(byte[]... bytes) {
// Initialize CountDownLatch
final CountDownLatch signal = new CountDownLatch(1);
StorageReference ref = App.getFireStorage().getReference();
ref = ref.child("review_images/" +
mDestinationId + "/" +
mExistingReview.getReviewId() + "/" +
mUploadProgressCount);
ref.putBytes(bytes[0]).addOnSuccessListener(taskSnapshot -> {
String url = taskSnapshot.getDownloadUrl().toString();
mExistingReview.getImages().add(url);
App.getFirestore().collection("reviews").document(mExistingReview.getReviewId()).set(mExistingReview);
mUploadProgressCount++;
// Start count down
signal.countDown();
});
// Wait for putBytes to return and handle case if
// threads get interrupted.
// You can also specify a maximum time to wait before
// displaying error to user (ie Try Again)
try {
signal.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
I have some pics to upload to the ftp server and I am using Asynctask for it.The images need to be sent to multiple host so I am using a for loop.The data to be passed is very well being fetched by the constructor but the doInBackground method is not running which was earlier running very well without the for loop and the additional data apart from the String filePathName that I am trying to pass in now in doInBackground.please help me
class uploadTask extends AsyncTask<String, Void, String> {
public uploadTask(String filePathName,String host_2,String user_2,String pass_2)
{
filePath=filePathName;
host_1=host_2;
user_1=user_2;
pass_1=pass_2;
Toast.makeText(getBaseContext(),"FTP DATA RECEIVING:"+"HOST:"+host_2+" USERNAME:"+user_2+" PASS:"+pass_2,Toast.LENGTH_LONG).show();
//hostName=host;
}
#Override
protected String doInBackground(String... params) {
try {
Toast.makeText(getBaseContext(),"Entered Do in Background Method to upload",Toast.LENGTH_SHORT).show();
ftp_host = "ftp.photoshelter.com";//This is not the correct way. Supposed to get from Backendless table
ftp_username = "brytest";//This is not the correct way. Supposed to get from Backendless table
ftp_password = "passtest";//This is not the correct way. Supposed to get from Backendless table
Toast.makeText(getBaseContext(),"HOST:"+ftp_host+" USERNAME:"+ftp_username+" PASS:"+ftp_password,Toast.LENGTH_LONG).show();
news_agency = "news agency";
easyFTP ftp = new easyFTP();
ftp.connect(ftp_host, ftp_username, ftp_password);
status = ftp.setWorkingDirectory("mem/images"); // if User say provided any Destination then Set it , otherwise
// Upload will be stored on Default /root level on server
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageTimeStamped = ftp_username + "_" + timeStamp + ".png";
FileInputStream is = new FileInputStream(imageFileLocation);
//addPhotoGrapherInfo();
ftp.uploadFile(is, imageTimeStamped);
System.out.println("Successfull ftp upload to " + ftp_host);
Toast.makeText(getBaseContext(), "Photo uploading by ftp to " + ftp_host, Toast.LENGTH_LONG).show();
//}
//reset booleans
//cameraPicTaken = false;
//galleryImageSelected = false;
//System.out.println("reset cameraPicTaken and galleryImageSelected");
// }
return new String("Upload Successful");
}catch (Exception e){
String t="Failure : " + e.getLocalizedMessage();
return t;
}
}
}
my onClickListener with for loop
if(cameraPicTaken || galleryImageSelected) {
Toast.makeText(SubmitActivity.this,"Image Location is:"+ imageFileLocation,Toast.LENGTH_LONG).show();
//addPhotoGrapherInfo();
for(int i=0;i<Common.selectedHostArray.size();i++) {
uploadFile(imageFileLocation,Common.selectedHostArray.get(i),Common.selectedUsernameArray.get(i),Common.selectedPasswordArray.get(i));
}
cameraPicTaken = false;
galleryImageSelected = false;
}
funnction called in onClick
public void uploadFile(String filePath,String host_1,String user_1,String pass_1)
{
if(cameraPicTaken == true) {
System.out.println("camera photo start upload");
//for(int i=0;i<Common.selectedHostArray.size();i++) {
//host_1=Common.selectedHostArray.get(i);
//user_1=Common.selectedUsernameArray.get(i);
//pass_1=Common.selectedPasswordArray.get(i);
//host_1="ftp.photoshelter.com";
//user_1="brytest";
//pass_1="passtest";
Toast.makeText(getBaseContext(),"FTP DATA PASSING:"+"HOST:"+host_1+" USERNAME:"+user_1+" PASS:"+pass_1,Toast.LENGTH_LONG).show();
new uploadTask(filePath,host_1,user_1,pass_1).execute();
// }
//cameraPicTaken = false;
//galleryImageSelected = false;
System.out.println("reset cameraPicTaken and galleryImageSelected");
//cameraPicTaken = false;
}
if(galleryImageSelected == true){
System.out.println("gallery image start upload");
Toast.makeText(getBaseContext(),"FTP DATA PASSING:"+"HOST:"+host_1+" USERNAME:"+user_1+" PASS:"+pass_1,Toast.LENGTH_LONG).show();
new uploadTask(filePath,host_1,user_1,pass_1).execute();
//new uploadTask(filePat)h.execute();
//galleryImageSelected = false;
}
Toast.makeText(getBaseContext(), "Photo uploading by ftp to photoshelter.com" /*+ news_agency*/, Toast.LENGTH_LONG).show();
}
You're trying to perform a UI command on a background thread (Toast). This is causing your background tasks to fail early. Since your background tasks catch their own errors, they fail silently.
#Override
protected String doInBackground(String... params) {
try {
// you can't Toast on a background thread, this should throw an exception
Toast.makeText(getBaseContext(),"Entered Do in Background Method to upload",Toast.LENGTH_SHORT).show();
...
}catch (Exception e){
// your Toast exception is getting caught silently here
String t="Failure : " + e.getLocalizedMessage();
return t;
}
}
By the way, the try/catch on everything is not a good practice. You end up with a ton of silent failures leaving you scratching your head and asking why things aren't working.
I've seen several questions on this but all of the solutions didn't work for me. For a client we have to develop an app that actually does nothing except of showing a WebView and a native DrawerLayout. However, we only have their mobile webpage (with a menu, etc.). So we have to hide some elements. It is very important that the existing stylesheets stay the same, just some other CSS rules are added.
What I've tried so far:
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (!mErrorOccured && !noConnectionAvailable) {
injectCSS();
}
mainActivity.hideLoadingScreen();
}
With this Injection:
// Inject CSS method: read style.css/readmode.css from assets folder
// Append stylesheet to document head
private void injectCSS() {
try {
Activity activity = (Activity) mContext;
SharedPreferences sharPref = activity.getSharedPreferences(Constants.PREFERENCE_NAME, Context.MODE_PRIVATE);
Boolean isReadMode = sharPref.getBoolean(Constants.READMODE_KEY, false);
InputStream inputStream;
if (isReadMode) {
inputStream = activity.getAssets().open("readmode.css");
} else {
inputStream = activity.getAssets().open("style.css");
}
byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
inputStream.close();
String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP);
webView.loadUrl("javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var style = document.createElement('style');" +
"style.type = 'text/css';" +
// Tell the browser to BASE64-decode the string into your script !!!
"style.innerHTML = window.atob('" + encoded + "');" +
"parent.appendChild(style);" +
"Android.injectCSS('Works!');})()");
} catch (Exception e) {
e.printStackTrace();
}
}
I've also tried to add a JavaScript Interface that uses the Android.injectCSS('Works!'); of the JavaScript above combined with:
#JavascriptInterface
public void injectCSS(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_LONG).show();
mMainActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
mMainFragment.unhideWebView();
}
});
}
And:
public void unhideWebView() {
webView.setVisibility(View.VISIBLE);
}
However, there is always a delay before the elements of the web page are hidden. I've also tried to use Jsoup. First this threw an NetworkOnMainThreadException. After I've tried to use it with an AsyncTask, it was not possible to change the WebView on onPostExecute() because this handling must be on the main thread. Even using a runOnUiThrad() did not help calling loadData() on the WebView with the new loaded data.
Is there any way to inject CSS/JS before the WebView shows up?