/**
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
/**
This file is part of Save For Offline, an Android app which saves / downloads complete webpages for offine reading.
**/
/**
If you modify, redistribute, or write something based on this or parts of it, you MUST,
I repeat, you MUST comply with the GPLv2+ license. This means that if you use or modify
my code, you MUST release the source code of your modified version, if / when this is
required under the terms of the license.
If you cannot / do not want to do this, DO NOT USE MY CODE. Thanks.
(I've added this message to to the source because it's been used in severeral proprietary
closed source apps, which I don't want, and which is also a violation of the liense.)
**/
/**
Written by Jonas Czech (JonasCz, stackoverflow.com/users/4428462/JonasCz and github.com/JonasCz). (4428462jonascz/eafc4d1afq)
**/
package jonas.tool.saveForOffline;
import android.app.*;
import android.os.*;
import android.webkit.*;
import android.view.*;
import android.content.*;
import android.net.*;
import android.widget.*;
import android.database.sqlite.*;
import android.util.*;
import java.io.*;
import android.preference.*;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
public class ViewActivity extends Activity {
private Intent incomingIntent;
private SharedPreferences preferences;
private String title;
private String fileLocation;
private String date;
private WebView webview;
private WebView.HitTestResult result;
private boolean invertedRendering;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
incomingIntent = getIntent();
preferences = PreferenceManager.getDefaultSharedPreferences(this);
if (preferences.getBoolean("dark_mode", false)) {
setTheme(android.R.style.Theme_Holo);
}
setContentView(R.layout.view_activity);
title = incomingIntent.getStringExtra(Database.TITLE);
fileLocation = incomingIntent.getStringExtra(Database.FILE_LOCATION);
date = incomingIntent.getStringExtra(Database.TIMESTAMP);
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setSubtitle(incomingIntent.getStringExtra(Database.TITLE));
setProgressBarIndeterminateVisibility(true);
webview = (WebView) findViewById(R.id.webview);
setupWebView();
invertedRendering = preferences.getBoolean("dark_mode", false);
webview.loadUrl("file://" + fileLocation);
}
@Override
protected void onResume() {
super.onResume();
//set up inverted rendering, aka. night mode, if enabled.
if (invertedRendering) {
float[] mNegativeColorArray = {
-1.0f, 0, 0, 0, 255, // red
0, -1.0f, 0, 0, 255, // green
0, 0, -1.0f, 0, 255, // blue
0, 0, 0, 1.0f, 0 // alpha
};
Paint mPaint = new Paint();
ColorMatrixColorFilter filterInvert = new ColorMatrixColorFilter(mNegativeColorArray);
mPaint.setColorFilter(filterInvert);
webview.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
}
}
private void setupWebView() {
String ua = preferences.getString("user_agent", "mobile");
boolean javaScriptEnabled = preferences.getBoolean("enable_javascript", true);
registerForContextMenu(webview);
webview.getSettings().setUserAgentString(ua);
webview.getSettings().setLoadWithOverviewMode(true);
webview.getSettings().setUseWideViewPort(true);
webview.getSettings().setJavaScriptEnabled(javaScriptEnabled);
webview.getSettings().setBuiltInZoomControls(true);
webview.getSettings().setDisplayZoomControls(false);
webview.getSettings().setAllowFileAccess(true);
webview.getSettings().setAllowFileAccessFromFileURLs(true);
webview.getSettings().setMediaPlaybackRequiresUserGesture(false);
webview.getSettings().setDefaultTextEncodingName("UTF-8");
webview.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url){
setProgressBarIndeterminateVisibility(false);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
try {
//send the user to installed browser instead of opening in the app, as per issue 19.
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
} catch (Exception e) {
//Activity not found or bad url
e.printStackTrace();
return false;
}
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
if (!url.startsWith("file") && preferences.getBoolean("offline_sandbox_mode", false)) {
Log.w("ViewActivity", "Request blocked: " + url);
return new WebResourceResponse(null, null, null);
} else {
return null;
}
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.view_activity_actions, menu);
return super.onCreateOptionsMenu(menu);
}
public boolean onOptionsItemSelected(MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.ic_action_settings:
Intent settings = new Intent(getApplicationContext(), Preferences.class);
startActivityForResult(settings, 1);
return true;
case R.id.action_save_page_properties:
showPropertiesDialog();
return true;
case R.id.action_open_in_external:
Intent incomingIntent = getIntent();
Uri uri = Uri.parse(incomingIntent.getStringExtra(Database.ORIGINAL_URL));
Intent startBrowserIntent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(startBrowserIntent);
return true;
case R.id.action_open_file_in_external:
Intent newIntent = new Intent(Intent.ACTION_VIEW);
newIntent.setDataAndType(Uri.fromFile(new File(fileLocation)), "text/html");
newIntent.setFlags(newIntent.FLAG_ACTIVITY_NEW_TASK);
try {
startActivity(newIntent);
} catch (android.content.ActivityNotFoundException e) {
Toast.makeText(this, "No installed app can open HTML files", Toast.LENGTH_LONG).show();
}
return true;
case R.id.action_delete:
AlertDialog.Builder build;
build = new AlertDialog.Builder(ViewActivity.this);
build.setTitle("Delete ?");
build.setMessage(title);
build.setPositiveButton("Delete",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
SQLiteDatabase dataBase = new Database(ViewActivity.this).getWritableDatabase();
Intent incomingIntent2 = getIntent();
dataBase.delete(Database.TABLE_NAME, Database.ID + "=" + incomingIntent2.getStringExtra(Database.ID), null);
String fileLocation = incomingIntent2.getStringExtra(Database.FILE_LOCATION);
DirectoryHelper.deleteDirectory(new File(fileLocation).getParentFile());
Toast.makeText(ViewActivity.this, "Saved page deleted", Toast.LENGTH_LONG).show();
finish();
}
});
build.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog alert = build.create();
alert.show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void showPropertiesDialog() {
AlertDialog.Builder build = new AlertDialog.Builder(this);
build.setTitle("Details of saved page");
View layout = getLayoutInflater().inflate(R.layout.properties_dialog, null);
build.setView(layout);
TextView t = (TextView) layout.findViewById(R.id.properties_dialog_text_title);
t.setText("Title: \r\n" + title);
t = (TextView) layout.findViewById(R.id.properties_dialog_text_file_location);
t.setText("File location: \r\n" + fileLocation);
t = (TextView) layout.findViewById(R.id.properties_dialog_text_date);
t.setText("Date & Time saved: \r\n" + date);
t = (TextView) layout.findViewById(R.id.properties_dialog_text_orig_url);
t.setText("Saved from: \r\n" + incomingIntent.getStringExtra(Database.ORIGINAL_URL));
build.setPositiveButton("Close",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
build.setNeutralButton("Copy file location to clipboard", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(webview.getTitle(), fileLocation);
clipboard.setPrimaryClip(clip);
Toast.makeText(ViewActivity.this, "File location copied to clipboard", Toast.LENGTH_SHORT).show();
}
});
AlertDialog alert = build.create();
alert.show();
}
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
result = webview.getHitTestResult();
if (result.getType() == WebView.HitTestResult.ANCHOR_TYPE || result.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE || result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
// Menu options for a hyperlink.
//set the header title to the link url
menu.setHeaderTitle(result.getExtra());
menu.add(3, 3, 3, "Save Link");
menu.add(4, 4, 4, "Share Link");
menu.add(6, 6, 6, "Copy Link to clipboard");
menu.add(5, 5, 5, "Open Link");
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
if (item.getItemId() == 5) {
Uri uri = Uri.parse(result.getExtra());
Intent startBrowserIntent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(startBrowserIntent);
} else if (item.getItemId() == 4) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_TITLE, webview.getTitle());
i.putExtra(Intent.EXTRA_TEXT, result.getExtra());
startActivity(Intent.createChooser(i, "Share Link via"));
} else if (item.getItemId() == 3) {
Intent intent = new Intent(this, SaveService.class);
intent.putExtra(Intent.EXTRA_TEXT, result.getExtra());
startService(intent);
} else if (item.getItemId() == 6) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(webview.getTitle(), result.getExtra());
clipboard.setPrimaryClip(clip);
Toast.makeText(this, "Link copied to clipboard", Toast.LENGTH_SHORT).show();
}
return super.onContextItemSelected(item);
}
}