libpdfviewer: Genesis
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "pdfjs-dist"]
|
||||
path = third_party/pdfjs-dist
|
||||
path = app/pdfjs-dist
|
||||
url = https://github.com/mozilla/pdfjs-dist.git
|
||||
|
@ -9,7 +9,7 @@ if (useKeystoreProperties) {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("com.android.library")
|
||||
id("kotlin-android")
|
||||
}
|
||||
|
||||
@ -29,21 +29,13 @@ android {
|
||||
buildToolsVersion = "32.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "org.grapheneos.pdfviewer"
|
||||
minSdk = 26
|
||||
minSdk = 21
|
||||
targetSdk = 31
|
||||
versionCode = 10
|
||||
versionName = versionCode.toString()
|
||||
resourceConfigurations.add("en")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
getByName("debug") {
|
||||
applicationIdSuffix = ".debug"
|
||||
}
|
||||
|
||||
getByName("release") {
|
||||
isShrinkResources = true
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
if (useKeystoreProperties) {
|
||||
|
@ -2,25 +2,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.grapheneos.pdfviewer"
|
||||
android:targetSandboxVersion="2">
|
||||
<application android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
android:allowBackup="true">
|
||||
<activity android:name=".PdfViewer"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:mimeType="application/pdf" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<application>
|
||||
<meta-data android:name="android.webkit.WebView.MetricsOptOut"
|
||||
android:value="true" />
|
||||
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
|
||||
|
@ -1 +1 @@
|
||||
../../../../third_party/pdfjs-dist/build/pdf.min.js
|
||||
../../../pdfjs-dist/build/pdf.min.js
|
2
app/src/main/assets/pdf.worker.js
vendored
@ -1 +1 @@
|
||||
../../../../third_party/pdfjs-dist/build/pdf.worker.min.js
|
||||
../../../pdfjs-dist/build/pdf.worker.min.js
|
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 11 KiB |
@ -2,8 +2,6 @@ package org.grapheneos.pdfviewer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.view.View;
|
||||
|
||||
@ -13,7 +11,6 @@ import android.view.View;
|
||||
|
||||
class GestureHelper {
|
||||
public interface GestureListener {
|
||||
boolean onTapUp();
|
||||
// Can be replaced with ratio when supported
|
||||
void onZoomIn(float value);
|
||||
void onZoomOut(float value);
|
||||
@ -22,15 +19,6 @@ class GestureHelper {
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
static void attach(Context context, View gestureView, GestureListener listener) {
|
||||
|
||||
final GestureDetector detector = new GestureDetector(context,
|
||||
new GestureDetector.SimpleOnGestureListener() {
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent motionEvent) {
|
||||
return listener.onTapUp();
|
||||
}
|
||||
});
|
||||
|
||||
final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context,
|
||||
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
final float SPAN_RATIO = 600;
|
||||
@ -67,7 +55,6 @@ class GestureHelper {
|
||||
});
|
||||
|
||||
gestureView.setOnTouchListener((view, motionEvent) -> {
|
||||
detector.onTouchEvent(motionEvent);
|
||||
scaleDetector.onTouchEvent(motionEvent);
|
||||
return false;
|
||||
});
|
||||
|
@ -1,18 +1,16 @@
|
||||
package org.grapheneos.pdfviewer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.webkit.WebResourceRequest;
|
||||
@ -24,13 +22,11 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import org.grapheneos.pdfviewer.databinding.WebviewBinding;
|
||||
import org.grapheneos.pdfviewer.fragment.DocumentPropertiesFragment;
|
||||
import org.grapheneos.pdfviewer.fragment.JumpToPageFragment;
|
||||
import org.grapheneos.pdfviewer.loader.DocumentPropertiesLoader;
|
||||
@ -40,13 +36,9 @@ import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class PdfViewer extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<CharSequence>> {
|
||||
public class PdfViewer extends WebView implements LoaderManager.LoaderCallbacks<List<CharSequence>> {
|
||||
public static final String TAG = "PdfViewer";
|
||||
|
||||
private static final String STATE_URI = "uri";
|
||||
private static final String STATE_PAGE = "page";
|
||||
private static final String STATE_ZOOM_RATIO = "zoomRatio";
|
||||
private static final String STATE_DOCUMENT_ORIENTATION_DEGREES = "documentOrientationDegrees";
|
||||
private static final String KEY_PROPERTIES = "properties";
|
||||
|
||||
private static final String CONTENT_SECURITY_POLICY =
|
||||
@ -92,12 +84,10 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
private static final float MAX_ZOOM_RATIO = 1.5f;
|
||||
private static final int ALPHA_LOW = 130;
|
||||
private static final int ALPHA_HIGH = 255;
|
||||
private static final int ACTION_OPEN_DOCUMENT_REQUEST_CODE = 1;
|
||||
private static final int STATE_LOADED = 1;
|
||||
private static final int STATE_END = 2;
|
||||
private static final int PADDING = 10;
|
||||
|
||||
private Uri mUri;
|
||||
public int mPage;
|
||||
public int mNumPages;
|
||||
private float mZoomRatio = 1f;
|
||||
@ -107,70 +97,27 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
private List<CharSequence> mDocumentProperties;
|
||||
private InputStream mInputStream;
|
||||
|
||||
private WebviewBinding binding;
|
||||
private TextView mTextView;
|
||||
private Toast mToast;
|
||||
private Snackbar snackbar;
|
||||
|
||||
private class Channel {
|
||||
@JavascriptInterface
|
||||
public int getWindowInsetTop() {
|
||||
return windowInsetTop;
|
||||
}
|
||||
public AppCompatActivity activity;
|
||||
String fileName;
|
||||
Long fileSize;
|
||||
|
||||
@JavascriptInterface
|
||||
public int getPage() {
|
||||
return mPage;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public float getZoomRatio() {
|
||||
return mZoomRatio;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public int getDocumentOrientationDegrees() {
|
||||
return mDocumentOrientationDegrees;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setNumPages(int numPages) {
|
||||
mNumPages = numPages;
|
||||
runOnUiThread(PdfViewer.this::invalidateOptionsMenu);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setDocumentProperties(final String properties) {
|
||||
if (mDocumentProperties != null) {
|
||||
throw new SecurityException("mDocumentProperties not null");
|
||||
}
|
||||
|
||||
final Bundle args = new Bundle();
|
||||
args.putString(KEY_PROPERTIES, properties);
|
||||
runOnUiThread(() -> LoaderManager.getInstance(PdfViewer.this).restartLoader(DocumentPropertiesLoader.ID, args, PdfViewer.this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint({"SetJavaScriptEnabled", "ClickableViewAccessibility"})
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = WebviewBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
binding.webview.setBackgroundColor(Color.TRANSPARENT);
|
||||
void init(Context context) {
|
||||
setBackgroundColor(Color.TRANSPARENT);
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
WebView.setWebContentsDebuggingEnabled(true);
|
||||
}
|
||||
|
||||
binding.webview.setOnApplyWindowInsetsListener((view, insets) -> {
|
||||
setOnApplyWindowInsetsListener((view, insets) -> {
|
||||
windowInsetTop = insets.getSystemWindowInsetTop();
|
||||
binding.webview.evaluateJavascript("updateInset()", null);
|
||||
evaluateJavascript("updateInset()", null);
|
||||
return insets;
|
||||
});
|
||||
|
||||
final WebSettings settings = binding.webview.getSettings();
|
||||
final WebSettings settings = getSettings();
|
||||
settings.setAllowContentAccess(false);
|
||||
settings.setAllowFileAccess(false);
|
||||
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
|
||||
@ -178,12 +125,12 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
|
||||
CookieManager.getInstance().setAcceptCookie(false);
|
||||
|
||||
binding.webview.addJavascriptInterface(new Channel(), "channel");
|
||||
addJavascriptInterface(new Channel(), "channel");
|
||||
|
||||
binding.webview.setWebViewClient(new WebViewClient() {
|
||||
setWebViewClient(new WebViewClient() {
|
||||
private WebResourceResponse fromAsset(final String mime, final String path) {
|
||||
try {
|
||||
InputStream inputStream = getAssets().open(path.substring(1));
|
||||
InputStream inputStream = context.getAssets().open(path.substring(1));
|
||||
return new WebResourceResponse(mime, null, inputStream);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
@ -237,30 +184,12 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
mDocumentState = STATE_LOADED;
|
||||
invalidateOptionsMenu();
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
});
|
||||
|
||||
GestureHelper.attach(PdfViewer.this, binding.webview,
|
||||
GestureHelper.attach(context, this,
|
||||
new GestureHelper.GestureListener() {
|
||||
@Override
|
||||
public boolean onTapUp() {
|
||||
if (mUri != null) {
|
||||
binding.webview.evaluateJavascript("isTextSelected()", selection -> {
|
||||
if (!Boolean.parseBoolean(selection)) {
|
||||
if ((getWindow().getDecorView().getSystemUiVisibility() &
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
||||
hideSystemUi();
|
||||
} else {
|
||||
showSystemUi();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onZoomIn(float value) {
|
||||
zoomIn(value, false);
|
||||
@ -277,56 +206,82 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
}
|
||||
});
|
||||
|
||||
mTextView = new TextView(this);
|
||||
mTextView = new TextView(context);
|
||||
mTextView.setBackgroundColor(Color.DKGRAY);
|
||||
mTextView.setTextColor(ColorStateList.valueOf(Color.WHITE));
|
||||
mTextView.setTextSize(18);
|
||||
mTextView.setPadding(PADDING, 0, PADDING, 0);
|
||||
}
|
||||
|
||||
// If loaders are not being initialized in onCreate(), the result will not be delivered
|
||||
// after orientation change (See FragmentHostCallback), thus initialize the
|
||||
// loader manager impl so that the result will be delivered.
|
||||
LoaderManager.getInstance(this);
|
||||
public PdfViewer(@NonNull Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
snackbar = Snackbar.make(binding.webview, "", Snackbar.LENGTH_LONG);
|
||||
public PdfViewer(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
final Intent intent = getIntent();
|
||||
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
|
||||
if (!"application/pdf".equals(intent.getType())) {
|
||||
snackbar.setText(R.string.invalid_mime_type).show();
|
||||
return;
|
||||
}
|
||||
mUri = intent.getData();
|
||||
mPage = 1;
|
||||
public PdfViewer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public void onCreateOptionMenu(Menu menu) {
|
||||
MenuInflater inflater = activity.getMenuInflater();
|
||||
inflater.inflate(R.menu.pdf_viewer, menu);
|
||||
}
|
||||
|
||||
private class Channel {
|
||||
@JavascriptInterface
|
||||
public int getWindowInsetTop() {
|
||||
return windowInsetTop;
|
||||
}
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mUri = savedInstanceState.getParcelable(STATE_URI);
|
||||
mPage = savedInstanceState.getInt(STATE_PAGE);
|
||||
mZoomRatio = savedInstanceState.getFloat(STATE_ZOOM_RATIO);
|
||||
mDocumentOrientationDegrees = savedInstanceState.getInt(STATE_DOCUMENT_ORIENTATION_DEGREES);
|
||||
@JavascriptInterface
|
||||
public int getPage() {
|
||||
return mPage;
|
||||
}
|
||||
|
||||
if (mUri != null) {
|
||||
if ("file".equals(mUri.getScheme())) {
|
||||
snackbar.setText(R.string.legacy_file_uri).show();
|
||||
return;
|
||||
@JavascriptInterface
|
||||
public float getZoomRatio() {
|
||||
return mZoomRatio;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public int getDocumentOrientationDegrees() {
|
||||
return mDocumentOrientationDegrees;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setNumPages(int numPages) {
|
||||
mNumPages = numPages;
|
||||
activity.runOnUiThread(activity::invalidateOptionsMenu);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setDocumentProperties(final String properties) {
|
||||
if (mDocumentProperties != null) {
|
||||
throw new SecurityException("mDocumentProperties not null");
|
||||
}
|
||||
|
||||
loadPdf();
|
||||
final Bundle args = new Bundle();
|
||||
args.putString(KEY_PROPERTIES, properties);
|
||||
activity.runOnUiThread(() -> LoaderManager.getInstance(PdfViewer.this.activity).restartLoader(DocumentPropertiesLoader.ID, args, PdfViewer.this));
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Loader<List<CharSequence>> onCreateLoader(int id, Bundle args) {
|
||||
return new DocumentPropertiesLoader(this, args.getString(KEY_PROPERTIES), mNumPages, mUri);
|
||||
return new DocumentPropertiesLoader(activity, args.getString(KEY_PROPERTIES), mNumPages, fileName, fileSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(@NonNull Loader<List<CharSequence>> loader, List<CharSequence> data) {
|
||||
mDocumentProperties = data;
|
||||
LoaderManager.getInstance(this).destroyLoader(DocumentPropertiesLoader.ID);
|
||||
LoaderManager.getInstance(activity).destroyLoader(DocumentPropertiesLoader.ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -334,23 +289,18 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
mDocumentProperties = null;
|
||||
}
|
||||
|
||||
private void loadPdf() {
|
||||
try {
|
||||
if (mInputStream != null) {
|
||||
mInputStream.close();
|
||||
}
|
||||
mInputStream = getContentResolver().openInputStream(mUri);
|
||||
} catch (IOException e) {
|
||||
snackbar.setText(R.string.io_error).show();
|
||||
return;
|
||||
}
|
||||
|
||||
showSystemUi();
|
||||
binding.webview.loadUrl("https://localhost/viewer.html");
|
||||
public void loadPdf(InputStream inputStream, String fileName, Long fileSize) {
|
||||
mPage = 1;
|
||||
mDocumentProperties = null;
|
||||
mInputStream = inputStream;
|
||||
this.fileName = fileName;
|
||||
this.fileSize = fileSize;
|
||||
loadUrl("https://localhost/viewer.html");
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
private void renderPage(final int zoom) {
|
||||
binding.webview.evaluateJavascript("onRenderPage(" + zoom + ")", null);
|
||||
evaluateJavascript("onRenderPage(" + zoom + ")", null);
|
||||
}
|
||||
|
||||
private void documentOrientationChanged(final int orientationDegreesOffset) {
|
||||
@ -361,18 +311,11 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
renderPage(0);
|
||||
}
|
||||
|
||||
private void openDocument() {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("application/pdf");
|
||||
startActivityForResult(intent, ACTION_OPEN_DOCUMENT_REQUEST_CODE);
|
||||
}
|
||||
|
||||
private void zoomIn(float value, boolean end) {
|
||||
if (mZoomRatio < MAX_ZOOM_RATIO) {
|
||||
mZoomRatio = Math.min(mZoomRatio + value, MAX_ZOOM_RATIO);
|
||||
renderPage(end ? 1 : 2);
|
||||
invalidateOptionsMenu();
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@ -380,7 +323,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
if (mZoomRatio > MIN_ZOOM_RATIO) {
|
||||
mZoomRatio = Math.max(mZoomRatio - value, MIN_ZOOM_RATIO);
|
||||
renderPage(end ? 1 : 2);
|
||||
invalidateOptionsMenu();
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,48 +346,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
mPage = selected_page;
|
||||
renderPage(0);
|
||||
showPageNumber();
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
private void showSystemUi() {
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
|
||||
private void hideSystemUi() {
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
savedInstanceState.putParcelable(STATE_URI, mUri);
|
||||
savedInstanceState.putInt(STATE_PAGE, mPage);
|
||||
savedInstanceState.putFloat(STATE_ZOOM_RATIO, mZoomRatio);
|
||||
savedInstanceState.putInt(STATE_DOCUMENT_ORIENTATION_DEGREES, mDocumentOrientationDegrees);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent resultData) {
|
||||
super.onActivityResult(requestCode, resultCode, resultData);
|
||||
|
||||
if (requestCode == ACTION_OPEN_DOCUMENT_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
|
||||
if (resultData != null) {
|
||||
mUri = resultData.getData();
|
||||
mPage = 1;
|
||||
mDocumentProperties = null;
|
||||
loadPdf();
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
activity.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@ -453,22 +355,13 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
mToast.cancel();
|
||||
}
|
||||
mTextView.setText(String.format("%s/%s", mPage, mNumPages));
|
||||
mToast = new Toast(getApplicationContext());
|
||||
mToast = new Toast(activity);
|
||||
mToast.setGravity(Gravity.BOTTOM | Gravity.END, PADDING, PADDING);
|
||||
mToast.setDuration(Toast.LENGTH_SHORT);
|
||||
mToast.setView(mTextView);
|
||||
mToast.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.pdf_viewer, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
final int[] ids = { R.id.action_zoom_in, R.id.action_zoom_out, R.id.action_jump_to_page,
|
||||
R.id.action_next, R.id.action_previous, R.id.action_first, R.id.action_last,
|
||||
@ -499,7 +392,6 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
final int itemId = item.getItemId();
|
||||
if (itemId == R.id.action_previous) {
|
||||
@ -514,9 +406,6 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
} else if (itemId == R.id.action_last) {
|
||||
onJumpToPageInDocument(mNumPages);
|
||||
return true;
|
||||
} else if (itemId == R.id.action_open) {
|
||||
openDocument();
|
||||
return true;
|
||||
} else if (itemId == R.id.action_zoom_out) {
|
||||
zoomOut(0.25f, true);
|
||||
return true;
|
||||
@ -532,14 +421,13 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||
} else if (itemId == R.id.action_view_document_properties) {
|
||||
DocumentPropertiesFragment
|
||||
.newInstance(mDocumentProperties)
|
||||
.show(getSupportFragmentManager(), DocumentPropertiesFragment.TAG);
|
||||
.show(activity.getSupportFragmentManager(), DocumentPropertiesFragment.TAG);
|
||||
return true;
|
||||
} else if (itemId == R.id.action_jump_to_page) {
|
||||
new JumpToPageFragment()
|
||||
.show(getSupportFragmentManager(), JumpToPageFragment.TAG);
|
||||
JumpToPageFragment.newInstance(this)
|
||||
.show(activity.getSupportFragmentManager(), JumpToPageFragment.TAG);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -20,15 +20,12 @@ public class JumpToPageFragment extends DialogFragment {
|
||||
private final static String STATE_PICKER_MAX = "picker_max";
|
||||
|
||||
private NumberPicker mPicker;
|
||||
PdfViewer pdfViewer;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
if (savedInstanceState != null) {
|
||||
mPicker.setMinValue(savedInstanceState.getInt(STATE_PICKER_MIN));
|
||||
mPicker.setMaxValue(savedInstanceState.getInt(STATE_PICKER_MAX));
|
||||
mPicker.setValue(savedInstanceState.getInt(STATE_PICKER_CUR));
|
||||
}
|
||||
public static JumpToPageFragment newInstance(PdfViewer pdfViewer) {
|
||||
JumpToPageFragment f = new JumpToPageFragment();
|
||||
f.pdfViewer = pdfViewer;
|
||||
return f;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -36,8 +33,8 @@ public class JumpToPageFragment extends DialogFragment {
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
mPicker = new NumberPicker(getActivity());
|
||||
mPicker.setMinValue(1);
|
||||
mPicker.setMaxValue(((PdfViewer)requireActivity()).mNumPages);
|
||||
mPicker.setValue(((PdfViewer)requireActivity()).mPage);
|
||||
mPicker.setMaxValue(pdfViewer.mNumPages);
|
||||
mPicker.setValue(pdfViewer.mPage);
|
||||
|
||||
final FrameLayout layout = new FrameLayout(getActivity());
|
||||
layout.addView(mPicker, new FrameLayout.LayoutParams(
|
||||
@ -49,7 +46,7 @@ public class JumpToPageFragment extends DialogFragment {
|
||||
.setView(layout)
|
||||
.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
|
||||
mPicker.clearFocus();
|
||||
((PdfViewer)requireActivity()).onJumpToPageInDocument(mPicker.getValue());
|
||||
pdfViewer.onJumpToPageInDocument(mPicker.getValue());
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create();
|
||||
|
@ -1,10 +1,7 @@
|
||||
package org.grapheneos.pdfviewer.loader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.StyleSpan;
|
||||
@ -12,6 +9,8 @@ import android.util.Log;
|
||||
|
||||
import androidx.loader.content.AsyncTaskLoader;
|
||||
|
||||
import org.grapheneos.pdfviewer.R;
|
||||
import org.grapheneos.pdfviewer.Utils;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@ -19,9 +18,6 @@ import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.grapheneos.pdfviewer.R;
|
||||
import org.grapheneos.pdfviewer.Utils;
|
||||
|
||||
public class DocumentPropertiesLoader extends AsyncTaskLoader<List<CharSequence>> {
|
||||
public static final String TAG = "DocumentPropertiesLoader";
|
||||
|
||||
@ -29,16 +25,17 @@ public class DocumentPropertiesLoader extends AsyncTaskLoader<List<CharSequence>
|
||||
|
||||
private final String mProperties;
|
||||
private final int mNumPages;
|
||||
private final Uri mUri;
|
||||
|
||||
private Cursor mCursor;
|
||||
String fileName;
|
||||
Long fileSize;
|
||||
|
||||
public DocumentPropertiesLoader(Context context, String properties, int numPages, Uri uri) {
|
||||
public DocumentPropertiesLoader(Context context, String properties, int numPages, String fileName, Long fileSize) {
|
||||
super(context);
|
||||
|
||||
mProperties = properties;
|
||||
mNumPages = numPages;
|
||||
mUri = uri;
|
||||
this.fileName = fileName;
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -48,23 +45,8 @@ public class DocumentPropertiesLoader extends AsyncTaskLoader<List<CharSequence>
|
||||
final String[] names = context.getResources().getStringArray(R.array.property_names);
|
||||
final List<CharSequence> properties = new ArrayList<>(names.length);
|
||||
|
||||
mCursor = context.getContentResolver().query(mUri, null, null, null, null);
|
||||
if (mCursor != null) {
|
||||
mCursor.moveToFirst();
|
||||
|
||||
final int indexName = mCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
if (indexName >= 0) {
|
||||
properties.add(getProperty(null, names[0], mCursor.getString(indexName)));
|
||||
}
|
||||
|
||||
final int indexSize = mCursor.getColumnIndex(OpenableColumns.SIZE);
|
||||
if (indexSize >= 0) {
|
||||
final long fileSize = Long.parseLong(mCursor.getString(indexSize));
|
||||
properties.add(getProperty(null, names[1], Utils.parseFileSize(fileSize)));
|
||||
}
|
||||
|
||||
mCursor.close();
|
||||
}
|
||||
properties.add(getProperty(null, names[0], fileName));
|
||||
properties.add(getProperty(null, names[1], Utils.parseFileSize(fileSize)));
|
||||
|
||||
try {
|
||||
final JSONObject json = new JSONObject(mProperties);
|
||||
@ -89,9 +71,7 @@ public class DocumentPropertiesLoader extends AsyncTaskLoader<List<CharSequence>
|
||||
|
||||
@Override
|
||||
public void deliverResult(List<CharSequence> properties) {
|
||||
if (isReset()) {
|
||||
onReleaseResources();
|
||||
} else if (isStarted()) {
|
||||
if (!isReset() && isStarted()) {
|
||||
super.deliverResult(properties);
|
||||
}
|
||||
}
|
||||
@ -106,26 +86,11 @@ public class DocumentPropertiesLoader extends AsyncTaskLoader<List<CharSequence>
|
||||
cancelLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled(List<CharSequence> properties) {
|
||||
super.onCanceled(properties);
|
||||
|
||||
onReleaseResources();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReset() {
|
||||
super.onReset();
|
||||
|
||||
onStopLoading();
|
||||
onReleaseResources();
|
||||
}
|
||||
|
||||
private void onReleaseResources() {
|
||||
if (mCursor != null) {
|
||||
mCursor.close();
|
||||
mCursor = null;
|
||||
}
|
||||
}
|
||||
|
||||
private CharSequence getProperty(final JSONObject json, String name, String specName) {
|
||||
|
Before Width: | Height: | Size: 153 B |
Before Width: | Height: | Size: 133 B |
Before Width: | Height: | Size: 206 B |
Before Width: | Height: | Size: 283 B |
Before Width: | Height: | Size: 372 B |
@ -1,30 +0,0 @@
|
||||
<!--
|
||||
Copyright (C) 2021 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group
|
||||
android:scaleX="2.43375"
|
||||
android:scaleY="2.43375"
|
||||
android:translateX="24.795"
|
||||
android:translateY="24.795">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M18,4L6,4C4.9,4 4,4.9 4,6v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,6C20,4.9 19.1,4 18,4ZM9.5,11.5C9.5,12.33 8.83,13 8,13L7,13v1.25C7,14.66 6.66,15 6.25,15 5.84,15 5.5,14.66 5.5,14.25L5.5,10c0,-0.55 0.45,-1 1,-1L8,9c0.83,0 1.5,0.67 1.5,1.5zM14.5,13.5c0,0.83 -0.67,1.5 -1.5,1.5h-2c-0.28,0 -0.5,-0.22 -0.5,-0.5v-5C10.5,9.22 10.72,9 11,9h2c0.83,0 1.5,0.67 1.5,1.5zM18.5,9.75c0,0.41 -0.34,0.75 -0.75,0.75L17,10.5v1h0.75c0.41,0 0.75,0.34 0.75,0.75 0,0.41 -0.34,0.75 -0.75,0.75L17,13v1.25C17,14.66 16.66,15 16.25,15 15.84,15 15.5,14.66 15.5,14.25L15.5,10c0,-0.55 0.45,-1 1,-1h1.25c0.41,0 0.75,0.34 0.75,0.75zM7,11.5h1v-1L7,10.5ZM12,13.5h1v-3h-1z" />
|
||||
</group>
|
||||
</vector>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/webview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
@ -19,12 +19,6 @@
|
||||
android:title="@string/action_next"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_open"
|
||||
android:icon="@drawable/ic_insert_drive_file_white_24dp"
|
||||
android:title="@string/action_open"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_zoom_out"
|
||||
android:icon="@drawable/ic_zoom_out_white_24dp"
|
||||
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:shrinkMode="strict" />
|
@ -1,9 +0,0 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:windowBackground">#000000</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
@ -4,7 +4,6 @@
|
||||
<!--<string name="action_settings">Settings</string>-->
|
||||
<string name="action_previous">Previous page</string>
|
||||
<string name="action_next">Next page</string>
|
||||
<string name="action_open">Open document</string>
|
||||
<string name="action_first">First page</string>
|
||||
<string name="action_last">Last page</string>
|
||||
<string name="action_zoom_out">Zoom out</string>
|
||||
|
@ -1,8 +0,0 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|