├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── cn │ │ └── xiaolong │ │ └── pdfiumpdfviewer │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── cn │ │ │ └── xiaolong │ │ │ └── pdfiumpdfviewer │ │ │ ├── MainActivity.java │ │ │ ├── download │ │ │ ├── ApiService.java │ │ │ ├── DownLoadInfo.java │ │ │ ├── DownLoadManager.java │ │ │ ├── DownloadInterceptor.java │ │ │ ├── DownloadProgressListener.java │ │ │ ├── DownloadResponseBody.java │ │ │ ├── HttpProgressOnNextListener.java │ │ │ ├── ProgressDownSubscriber.java │ │ │ └── exception │ │ │ │ ├── HttpTimeException.java │ │ │ │ └── RetryWhenNetworkException.java │ │ │ └── pdf │ │ │ ├── CircleProgressBar.java │ │ │ ├── PDFManager.java │ │ │ ├── PhotoViewPager.java │ │ │ ├── adapter │ │ │ ├── PdfGuideAdapter.java │ │ │ └── PdfImageAdapter.java │ │ │ ├── list │ │ │ └── ViewHolder.java │ │ │ ├── source │ │ │ ├── AssetSource.java │ │ │ ├── ByteArraySource.java │ │ │ ├── DocumentSource.java │ │ │ ├── FileSource.java │ │ │ ├── InputStreamSource.java │ │ │ └── UriSource.java │ │ │ └── utils │ │ │ ├── DownloadFile.java │ │ │ ├── DownloadFileUrlConnectionImpl.java │ │ │ ├── FileUtils.java │ │ │ ├── ScreenUtil.java │ │ │ └── Util.java │ └── res │ │ ├── drawable-xhdpi │ │ ├── ic_pack.png │ │ └── ic_unfolded.png │ │ ├── drawable │ │ ├── corner6_gray.xml │ │ ├── corner_gray.xml │ │ ├── select_pdf_back.xml │ │ ├── select_viewer_state.xml │ │ └── stroke_blue.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── item_viewpage_image.xml │ │ └── listitem_guide.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── cn │ └── xiaolong │ └── pdfiumpdfviewer │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lucky.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | 43 | # Keystore files 44 | *.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | 49 | # Google Services (e.g. APIs or Firebase) 50 | google-services.json 51 | 52 | # Freeline 53 | freeline.py 54 | freeline/ 55 | freeline_project_description.json -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 郭小龙 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CSDN](https://img.shields.io/badge/CSDN-@xiaolongonly-blue.svg?style=flat)](http://blog.csdn.net/guoxiaolongonly) 2 | [![CSDN](https://img.shields.io/badge/PersonBlog-@xiaolongonly-blue.svg?style=flat)](http://xiaolongonly.cn/) 3 | [![API](https://img.shields.io/badge/API-16%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=16) 4 | 5 | 6 | [![Screenshot of the sample app](https://github.com/xiaolongonly/PDFiumForAndroidDemo/blob/master/lucky.gif)](http://pre.im/b2h0) 7 | 8 | **PDFiumForAndroidDemo** 是一个基于[pdfium](https://pdfium.googlesource.com/pdfium/)的library [AndroidPdfViewer](https://github.com/barteksc/AndroidPdfViewer)写的一个demo。 9 | 10 | # 为什么写这个Demo? 11 | 12 | 当前流行的很多pdfViewer框架的封装,并不能支持定制化的界面,当交互和UI出完图的时候,你只能看着一堆框架懵逼。 13 | 如果你不知道你该选择哪个框架?在[AndroidPdf框架一览](http://blog.csdn.net/guoxiaolongonly/article/details/76992138)中会告诉你选择什么样的框架合适当前的需求。 14 | 15 | # 怎么使用它? 16 | 17 | 18 | ```java 19 | 20 | mPDFManager = new PDFManager.Builder(this) 21 | .pdfFromFile(downLoadPdfFile) 22 | //or pdfFromStream() pdfFromUri() pdfFormAsset() pdfFormByte() 23 | .setPassword() 24 | .setOnOpenErrorListener() 25 | .setOnOpenSuccessListener() 26 | .build(); 27 | 28 | ``` 29 | 30 | 正如上面所显示的,构建一个PDFManager只需要输入pdf的文件/流/字节码/资源文件位置/或者uri路径。PDFManager 提供了成功和失败的回调,并提供密码(如果pdf加密)。 31 | 32 | 然后你只需要获取你想要的pdf页面位置,还有图片的大小,就可以使用这张pdf页转成的图片了 33 | 34 | ```java 35 | 36 | mPDFManager.getPdfBitmapCustomSize(position, ScreenUtil.getScreenSize(context)[0] * 7 / 8) 37 | .observeOn(AndroidSchedulers.mainThread()) 38 | .subscribe(new Subscriber() { 39 | @Override 40 | public void onStart() { 41 | ivLargeImage.setImageResource(R.mipmap.ic_launcher); 42 | } 43 | 44 | @Override 45 | public void onCompleted() { 46 | Log.d("adapterloadCompleted", "completed"); 47 | } 48 | 49 | @Override 50 | public void onError(Throwable e) { 51 | } 52 | 53 | @Override 54 | public void onNext(Bitmap bitmap) { 55 | ivLargeImage.setImageBitmap(bitmap); 56 | } 57 | }); 58 | 59 | ``` 60 | 61 | 如果你没有更好的选择,那么尝试使用这个demo去做更好的封装吧。 62 | 63 | 64 | 65 | # License 66 | 67 | ``` 68 | 69 | MIT License 70 | 71 | ``` 72 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'me.tatarka.retrolambda' 3 | android { 4 | 5 | compileOptions { 6 | sourceCompatibility JavaVersion.VERSION_1_8 7 | targetCompatibility JavaVersion.VERSION_1_8 8 | } 9 | compileSdkVersion 25 10 | buildToolsVersion "25.0.3" 11 | defaultConfig { 12 | applicationId "cn.xiaolong.pdfiumpdfviewer" 13 | minSdkVersion 15 14 | targetSdkVersion 25 15 | versionCode 1 16 | versionName "1.0" 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | } 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 30 | exclude group: 'com.android.support', module: 'support-annotations' 31 | }) 32 | compile 'com.android.support:appcompat-v7:25.3.1' 33 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 34 | compile 'com.github.barteksc:pdfium-android:1.5.0' 35 | // Rx 36 | compile 'io.reactivex:rxjava:1.1.1' 37 | compile 'io.reactivex:rxandroid:1.1.0' 38 | // Retrofit网络请求 39 | compile 'com.squareup.retrofit2:retrofit:2.1.0' 40 | compile 'com.squareup.retrofit2:converter-gson:2.1.0' 41 | compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' 42 | compile 'com.github.chrisbanes.photoview:library:1.2.4' 43 | testCompile 'junit:junit:4.12' 44 | } 45 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\CRAWLER\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/cn/xiaolong/pdfiumpdfviewer/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("cn.xiaolong.pdfiumpdfviewer", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/MainActivity.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.view.ViewPager; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | import android.widget.ListView; 10 | import android.widget.TextView; 11 | import android.widget.Toast; 12 | 13 | import java.io.File; 14 | 15 | import cn.xiaolong.pdfiumpdfviewer.download.DownLoadInfo; 16 | import cn.xiaolong.pdfiumpdfviewer.download.DownLoadManager; 17 | import cn.xiaolong.pdfiumpdfviewer.download.HttpProgressOnNextListener; 18 | import cn.xiaolong.pdfiumpdfviewer.pdf.CircleProgressBar; 19 | import cn.xiaolong.pdfiumpdfviewer.pdf.PDFManager; 20 | import cn.xiaolong.pdfiumpdfviewer.pdf.adapter.PdfGuideAdapter; 21 | import cn.xiaolong.pdfiumpdfviewer.pdf.adapter.PdfImageAdapter; 22 | import cn.xiaolong.pdfiumpdfviewer.pdf.utils.FileUtils; 23 | 24 | public class MainActivity extends AppCompatActivity implements HttpProgressOnNextListener { 25 | 26 | private CircleProgressBar cpbLoad; 27 | private PDFManager mPDFManager; 28 | private ViewPager mViewpager; 29 | private ListView mListView; 30 | private View vGuide; 31 | private File downLoadPdfFile; 32 | 33 | 34 | @Override 35 | protected void onCreate(@Nullable Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | setContentView(R.layout.activity_main); 38 | init(); 39 | setListener(); 40 | } 41 | 42 | protected void init() { 43 | cpbLoad = (CircleProgressBar) findViewById(R.id.cpbLoad); 44 | vGuide = findViewById(R.id.vGuide); 45 | vGuide.setVisibility(View.GONE); 46 | initcpbConfig(); 47 | 48 | downLoadPdfFile = new File(this.getCacheDir(), "lucky" + ".pdf"); 49 | if (downLoadPdfFile.exists() && FileUtils.getFileSize(downLoadPdfFile) > 0) { 50 | loadFinish(); 51 | } else { 52 | DownLoadManager.getDownLoadManager().startDown(getDownLoadInfo()); 53 | } 54 | } 55 | 56 | private void initcpbConfig() { 57 | cpbLoad.setValue(0); 58 | cpbLoad.setTextColor(getResources().getColor(R.color.main_normal_black_color)); 59 | cpbLoad.setProdressWidth(cpbLoad.dp2px(8)); 60 | cpbLoad.setProgress(getResources().getColor(R.color.main_blue_color)); 61 | cpbLoad.setCircleBackgroud(Color.WHITE); 62 | cpbLoad.setPreProgress(Color.WHITE); 63 | } 64 | 65 | private void loadFinish() { 66 | mPDFManager = new PDFManager.Builder(this) 67 | .pdfFromFile(downLoadPdfFile) 68 | .setOnOpenErrorListener(t -> { 69 | cpbLoad.setValue(0); 70 | DownLoadManager.getDownLoadManager().startDown(getDownLoadInfo()); 71 | }) 72 | .setOnOpenSuccessListener(() -> { 73 | cpbLoad.setVisibility(View.GONE); 74 | vGuide.setVisibility(View.VISIBLE); 75 | }) 76 | .build(); 77 | vGuide.setVisibility(View.VISIBLE); 78 | initListView(); 79 | initViewPager(); 80 | } 81 | 82 | private void initViewPager() { 83 | mViewpager = (ViewPager) findViewById(R.id.viewpager); 84 | mViewpager.setAdapter(new PdfImageAdapter(this, mPDFManager)); 85 | 86 | final TextView textView = (TextView) findViewById(R.id.tvPosition); 87 | textView.setVisibility(View.VISIBLE); 88 | textView.setText(1 + "/" + mPDFManager.pageCount()); 89 | mViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 90 | @Override 91 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 92 | 93 | } 94 | 95 | @Override 96 | public void onPageSelected(int position) { 97 | textView.setText(position + 1 + "/" + mPDFManager.pageCount()); 98 | ((PdfGuideAdapter) mListView.getAdapter()).setStatePosition(position); 99 | mListView.smoothScrollToPosition(position); 100 | } 101 | 102 | @Override 103 | public void onPageScrollStateChanged(int state) { 104 | 105 | } 106 | }); 107 | } 108 | 109 | private void initListView() { 110 | mListView = (ListView) findViewById(R.id.lvGuide); 111 | mListView.setVisibility(View.GONE); 112 | PdfGuideAdapter pdfGuideAdapter = new PdfGuideAdapter(this, mPDFManager); 113 | pdfGuideAdapter.setOnItemClickListener(v -> mViewpager.setCurrentItem((int) v.getTag())); 114 | mListView.setAdapter(pdfGuideAdapter); 115 | } 116 | 117 | private DownLoadInfo getDownLoadInfo() { 118 | DownLoadInfo mDownLoadInfo = new DownLoadInfo(); 119 | /*下载回调*/ 120 | mDownLoadInfo.listener = this; 121 | mDownLoadInfo.savePath = downLoadPdfFile.getAbsolutePath(); 122 | mDownLoadInfo.url = "https://d11.baidupcs.com/file/d10b1184525be1fa2185cbd1b17f244e?bkt=p3-1400d10b1184525be1fa2185cbd1b17f244e538a176a0000001d530b&xcode=d99bdf2146c60fedd1163b4354f9afd7c85d2bd05878864c0b2977702d3e6764&fid=1695803216-250528-664972808641582&time=1502263894&sign=FDTAXGERLBHS-DCb740ccc5511e5e8fedcff06b081203-8jadaXCWaHHtT4Uj%2F1bjxk9al74%3D&to=d11&size=1921803&sta_dx=1921803&sta_cs=283546&sta_ft=pdf&sta_ct=7&sta_mt=7&fm2=MH,Yangquan,Netizen-anywhere,,fujian,ct&newver=1&newfm=1&secfm=1&flow_ver=3&pkey=1400d10b1184525be1fa2185cbd1b17f244e538a176a0000001d530b&sl=76480590&expires=8h&rt=sh&r=723264417&mlogid=5117425091165905134&vuk=1695803216&vbdid=1114076982&fin=%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E5%B8%B8%E7%94%A8%E8%AF%8D%E6%B1%87%28%E5%8C%97%E4%BA%AC%E5%B0%9A%E5%AD%A6%E5%A0%82%E5%8F%91%E5%B8%83%29.pdf&fn=%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E5%B8%B8%E7%94%A8%E8%AF%8D%E6%B1%87%28%E5%8C%97%E4%BA%AC%E5%B0%9A%E5%AD%A6%E5%A0%82%E5%8F%91%E5%B8%83%29.pdf&rtype=1&iv=0&dp-logid=5117425091165905134&dp-callid=0.1.1&hps=1&csl=80&csign=WryUYKeHbb5ItV4jNvcrPWowU%2Bo%3D&so=0&ut=6&uter=4&serv=0&by=themis"; 123 | return mDownLoadInfo; 124 | } 125 | 126 | protected void setListener() { 127 | vGuide.setOnClickListener(v -> { 128 | if (!vGuide.isSelected()) { 129 | mListView.setVisibility(View.VISIBLE); 130 | vGuide.setSelected(true); 131 | } else { 132 | mListView.setVisibility(View.GONE); 133 | vGuide.setSelected(false); 134 | } 135 | }); 136 | } 137 | 138 | @Override 139 | protected void onDestroy() { 140 | super.onDestroy(); 141 | DownLoadManager.getDownLoadManager().stopAllDown(); 142 | if (mPDFManager != null) { 143 | mPDFManager.recycle(); 144 | } 145 | } 146 | 147 | 148 | @Override 149 | public void onNext(DownLoadInfo downLoadInfo) { 150 | Toast.makeText(this, downLoadInfo.savePath, Toast.LENGTH_LONG).show(); 151 | loadFinish(); 152 | } 153 | 154 | @Override 155 | public void onLoadStart() { 156 | cpbLoad.setValue(0); 157 | cpbLoad.setVisibility(View.VISIBLE); 158 | } 159 | 160 | @Override 161 | public void onLoadComplete() { 162 | cpbLoad.setVisibility(View.GONE); 163 | } 164 | 165 | @Override 166 | public void updateProgress(long readLength, long countLength) { 167 | cpbLoad.setValue((int) (readLength * 100 / countLength)); 168 | } 169 | 170 | @Override 171 | public void onLoadError(Throwable e) { 172 | e.printStackTrace(); 173 | Toast.makeText(this, "下载发生异常错误。", Toast.LENGTH_LONG).show(); 174 | } 175 | 176 | @Override 177 | public void onLoadPause() { 178 | Toast.makeText(this, "暂停下载", Toast.LENGTH_LONG).show(); 179 | } 180 | 181 | @Override 182 | public void onLoadStop() { 183 | Toast.makeText(this, "停止下载", Toast.LENGTH_LONG).show(); 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/download/ApiService.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.download; 2 | 3 | 4 | import okhttp3.ResponseBody; 5 | import retrofit2.http.GET; 6 | import retrofit2.http.Header; 7 | import retrofit2.http.Streaming; 8 | import retrofit2.http.Url; 9 | import rx.Observable; 10 | 11 | public interface ApiService { 12 | 13 | /*断点续传下载接口*/ 14 | @Streaming/*大文件需要加入这个判断,防止下载过程中写入到内存中*/ 15 | @GET 16 | Observable download(@Header("RANGE") String start, @Url String url); 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/download/DownLoadInfo.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.download; 2 | 3 | 4 | /** 5 | * @author xiaolong 6 | * @version v1.0 7 | * @function <描述功能> 8 | * @date 2017/2/10-9:53 9 | */ 10 | public class DownLoadInfo { 11 | /*存储位置*/ 12 | public String savePath; 13 | /*下载url*/ 14 | public String url; 15 | /*文件总长度*/ 16 | public long countLength; 17 | /*下载长度*/ 18 | public long readLength; 19 | /*下载唯一的HttpService*/ 20 | public ApiService service; 21 | /*回调监听*/ 22 | public HttpProgressOnNextListener listener; 23 | /*下载状态*/ 24 | public DownLoadManager.DownState state; 25 | 26 | 27 | public String getBaseUrl() { 28 | String baseUrl = url; 29 | String head = ""; 30 | int index = baseUrl.indexOf("://"); 31 | if (index != -1) { 32 | head = baseUrl.substring(0, index + 3); 33 | baseUrl = baseUrl.substring(index + 3); 34 | } 35 | index = baseUrl.indexOf("/"); 36 | if (index != -1) { 37 | baseUrl = baseUrl.substring(0, index + 1); 38 | } 39 | return head + baseUrl; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/download/DownLoadManager.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.download; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.RandomAccessFile; 6 | import java.nio.MappedByteBuffer; 7 | import java.nio.channels.FileChannel; 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import cn.xiaolong.pdfiumpdfviewer.download.exception.HttpTimeException; 14 | import cn.xiaolong.pdfiumpdfviewer.download.exception.RetryWhenNetworkException; 15 | import okhttp3.OkHttpClient; 16 | import okhttp3.ResponseBody; 17 | import retrofit2.Retrofit; 18 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 19 | import rx.android.schedulers.AndroidSchedulers; 20 | import rx.schedulers.Schedulers; 21 | 22 | /** 23 | * @author xiaolong 24 | * @version v1.0 25 | * @function <描述功能> 26 | * @date 2017/2/10-9:51 27 | */ 28 | public class DownLoadManager { 29 | /*记录下载数据*/ 30 | private Set downInfos; 31 | /*回调sub队列*/ 32 | private HashMap subMap; 33 | 34 | private static DownLoadManager sDownLoadManager; 35 | 36 | 37 | private DownLoadManager() { 38 | downInfos = new HashSet<>(); 39 | subMap = new HashMap<>(); 40 | } 41 | 42 | public static DownLoadManager getDownLoadManager() { 43 | if (sDownLoadManager == null) { 44 | synchronized (DownLoadManager.class) { 45 | if (sDownLoadManager == null) { 46 | sDownLoadManager = new DownLoadManager(); 47 | } 48 | } 49 | } 50 | return sDownLoadManager; 51 | } 52 | 53 | /** 54 | * 开始下载 55 | */ 56 | public void startDown(DownLoadInfo info) { 57 | /*正在下载不处理*/ 58 | if (info == null || subMap.get(info.url) != null) { 59 | return; 60 | } 61 | /*添加回调处理类*/ 62 | ProgressDownSubscriber subscriber = new ProgressDownSubscriber(info); 63 | /*记录回调sub*/ 64 | subMap.put(info.url, subscriber); 65 | /*获取service,多次请求公用一个service*/ 66 | ApiService httpService; 67 | if (downInfos.contains(info)) { 68 | httpService = info.service; 69 | } else { 70 | DownloadInterceptor interceptor = new DownloadInterceptor(subscriber); 71 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 72 | //手动创建一个OkHttpClient并设置超时时间 73 | builder.connectTimeout(30, TimeUnit.SECONDS); 74 | builder.addInterceptor(interceptor); 75 | builder.readTimeout(30, TimeUnit.SECONDS); 76 | Retrofit retrofit = new Retrofit.Builder() 77 | .client(builder.build()) 78 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 79 | .baseUrl(info.getBaseUrl()) 80 | .build(); 81 | httpService = retrofit.create(ApiService.class); 82 | info.service = httpService; 83 | } 84 | /*得到rx对象-上一次下載的位置開始下載*/ 85 | httpService.download("bytes=" + info.readLength + "-", info.url) 86 | /*指定线程*/ 87 | .subscribeOn(Schedulers.io()) 88 | .unsubscribeOn(Schedulers.io()) 89 | /*失败后的retry配置*/ 90 | .retryWhen(new RetryWhenNetworkException()) 91 | /*读取下载写入文件*/ 92 | .map(responseBody -> { 93 | try { 94 | writeCache(responseBody, new File(info.savePath), info); 95 | } catch (IOException e) { 96 | /*失败抛出异常*/ 97 | throw new HttpTimeException(e.getMessage()); 98 | } 99 | return info; 100 | }) 101 | /*回调线程*/ 102 | .observeOn(AndroidSchedulers.mainThread()) 103 | /*数据回调*/ 104 | .subscribe(subscriber); 105 | 106 | } 107 | 108 | 109 | /** 110 | * 停止下载 111 | */ 112 | public void stopDown(DownLoadInfo info) { 113 | if (info == null) return; 114 | info.state = DownState.STOP; 115 | info.listener.onLoadStop(); 116 | if (subMap.containsKey(info.url)) { 117 | ProgressDownSubscriber subscriber = subMap.get(info.url); 118 | subscriber.unsubscribe(); 119 | subMap.remove(info.url); 120 | } 121 | /*同步数据库*/ 122 | } 123 | 124 | 125 | /** 126 | * 删除 127 | * 128 | * @param info 129 | */ 130 | public void deleteDown(DownLoadInfo info) { 131 | stopDown(info); 132 | /*删除数据库信息和本地文件*/ 133 | } 134 | 135 | 136 | /** 137 | * 暂停下载 138 | * 139 | * @param info 140 | */ 141 | public void pause(DownLoadInfo info) { 142 | if (info == null) return; 143 | info.state = DownState.PAUSE; 144 | info.listener.onLoadPause(); 145 | if (subMap.containsKey(info.url)) { 146 | ProgressDownSubscriber subscriber = subMap.get(info.url); 147 | subscriber.unsubscribe(); 148 | subMap.remove(info.url); 149 | } 150 | /*这里需要讲info信息写入到数据中,可自由扩展,用自己项目的数据库*/ 151 | } 152 | 153 | /** 154 | * 停止全部下载 155 | */ 156 | public void stopAllDown() { 157 | for (DownLoadInfo downInfo : downInfos) { 158 | stopDown(downInfo); 159 | } 160 | subMap.clear(); 161 | downInfos.clear(); 162 | } 163 | 164 | /** 165 | * 暂停全部下载 166 | */ 167 | public void pauseAll() { 168 | for (DownLoadInfo downInfo : downInfos) { 169 | pause(downInfo); 170 | } 171 | subMap.clear(); 172 | downInfos.clear(); 173 | } 174 | 175 | 176 | /** 177 | * 返回全部正在下载的数据 178 | * 179 | * @return 180 | */ 181 | public Set getDownInfos() { 182 | return downInfos; 183 | } 184 | 185 | 186 | /** 187 | * 写入文件 188 | * 189 | * @param file 190 | * @param info 191 | * @throws IOException 192 | */ 193 | public void writeCache(ResponseBody responseBody, File file, DownLoadInfo info) throws IOException { 194 | if (!file.getParentFile().exists()) 195 | file.getParentFile().mkdirs(); 196 | long allLength; 197 | if (info.countLength == 0) { 198 | allLength = responseBody.contentLength(); 199 | } else { 200 | allLength = info.countLength; 201 | } 202 | FileChannel channelOut = null; 203 | RandomAccessFile randomAccessFile = null; 204 | randomAccessFile = new RandomAccessFile(file, "rwd"); 205 | channelOut = randomAccessFile.getChannel(); 206 | MappedByteBuffer mappedBuffer = channelOut.map(FileChannel.MapMode.READ_WRITE, 207 | info.readLength, allLength - info.readLength); 208 | byte[] buffer = new byte[1024 * 8]; 209 | int len; 210 | int record = 0; 211 | while ((len = responseBody.byteStream().read(buffer)) != -1) { 212 | mappedBuffer.put(buffer, 0, len); 213 | record += len; 214 | } 215 | responseBody.byteStream().close(); 216 | if (channelOut != null) { 217 | channelOut.close(); 218 | } 219 | if (randomAccessFile != null) { 220 | randomAccessFile.close(); 221 | } 222 | } 223 | 224 | public enum DownState { 225 | START, 226 | DOWN, 227 | PAUSE, 228 | STOP, 229 | ERROR, 230 | FINISH, 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/download/DownloadInterceptor.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.download; 2 | 3 | 4 | import java.io.IOException; 5 | 6 | import okhttp3.HttpUrl; 7 | import okhttp3.Interceptor; 8 | import okhttp3.Request; 9 | import okhttp3.Response; 10 | 11 | /** 12 | * 成功回调处理 13 | * Created by WZG on 2016/10/20. 14 | */ 15 | public class DownloadInterceptor implements Interceptor { 16 | 17 | private DownloadProgressListener listener; 18 | 19 | public DownloadInterceptor(DownloadProgressListener listener) { 20 | this.listener = listener; 21 | } 22 | 23 | @Override 24 | public Response intercept(Chain chain) throws IOException { 25 | Request request = chain.request(); 26 | HttpUrl httpUrl = request.url().newBuilder().build(); 27 | request = request.newBuilder().url(httpUrl).build(); 28 | Response originalResponse = chain.proceed(request); 29 | return originalResponse.newBuilder() 30 | .body(new DownloadResponseBody(originalResponse.body(), listener)) 31 | .build(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/download/DownloadProgressListener.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.download; 2 | 3 | 4 | /** 5 | * 成功回调处理 6 | * Created by xl on 2016/10/20. 7 | */ 8 | public interface DownloadProgressListener { 9 | /** 10 | * 下载进度 11 | * @param read 12 | * @param count 13 | * @param done 14 | */ 15 | void update(long read, long count, boolean done); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/download/DownloadResponseBody.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.download; 2 | 3 | import java.io.IOException; 4 | 5 | import okhttp3.MediaType; 6 | import okhttp3.ResponseBody; 7 | import okio.Buffer; 8 | import okio.BufferedSource; 9 | import okio.ForwardingSource; 10 | import okio.Okio; 11 | import okio.Source; 12 | 13 | /** 14 | * 自定义进度的body 15 | * @author xiaolong 16 | * @version v1.0 17 | * @function <描述功能> 18 | */ 19 | public class DownloadResponseBody extends ResponseBody { 20 | 21 | private ResponseBody responseBody; 22 | private DownloadProgressListener progressListener; 23 | private BufferedSource bufferedSource; 24 | 25 | public DownloadResponseBody(ResponseBody responseBody, DownloadProgressListener progressListener) { 26 | this.responseBody = responseBody; 27 | this.progressListener = progressListener; 28 | } 29 | 30 | @Override 31 | public MediaType contentType() { 32 | return responseBody.contentType(); 33 | } 34 | 35 | @Override 36 | public long contentLength() { 37 | return responseBody.contentLength(); 38 | } 39 | 40 | @Override 41 | public BufferedSource source() { 42 | if (bufferedSource == null) { 43 | bufferedSource = Okio.buffer(source(responseBody.source())); 44 | } 45 | return bufferedSource; 46 | } 47 | 48 | private Source source(Source source) { 49 | return new ForwardingSource(source) { 50 | long totalBytesRead = 0L; 51 | 52 | @Override 53 | public long read(Buffer sink, long byteCount) throws IOException { 54 | long bytesRead = super.read(sink, byteCount); 55 | // read() returns the number of bytes read, or -1 if this source is exhausted. 56 | totalBytesRead += bytesRead != -1 ? bytesRead : 0; 57 | if (null != progressListener) { 58 | progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1); 59 | } 60 | return bytesRead; 61 | } 62 | }; 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/download/HttpProgressOnNextListener.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.download; 2 | 3 | /** 4 | * 下载过程中的回调处理 5 | * 6 | * @author xiaolong 7 | * @version v1.0 8 | * @function <描述功能> 9 | */ 10 | public interface HttpProgressOnNextListener { 11 | /** 12 | * 成功后回调方法 13 | * 14 | * @param t 15 | */ 16 | void onNext(T t); 17 | 18 | /** 19 | * 开始下载 20 | */ 21 | void onLoadStart(); 22 | 23 | /** 24 | * 完成下载 25 | */ 26 | void onLoadComplete(); 27 | 28 | 29 | /** 30 | * 下载进度 31 | * 32 | * @param readLength 33 | * @param countLength 34 | */ 35 | void updateProgress(long readLength, long countLength); 36 | 37 | /** 38 | * 失败或者错误方法 39 | * 主动调用,更加灵活 40 | * 41 | * @param e 42 | */ 43 | void onLoadError(Throwable e); 44 | 45 | /** 46 | * 暂停下载 47 | */ 48 | void onLoadPause(); 49 | 50 | /** 51 | * 停止下载销毁 52 | */ 53 | void onLoadStop(); 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/download/ProgressDownSubscriber.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.download; 2 | 3 | 4 | 5 | import java.lang.ref.WeakReference; 6 | 7 | import rx.Subscriber; 8 | import rx.android.schedulers.AndroidSchedulers; 9 | import rx.functions.Action1; 10 | 11 | /** 12 | * 用于在Http请求开始时,自动显示一个ProgressDialog 13 | * 在Http请求结束是,关闭ProgressDialog 14 | * 调用者自己对请求数据进行处理 15 | * 16 | * @author xiaolong 17 | * @version v1.0 18 | * @function <描述功能> 19 | */ 20 | public class ProgressDownSubscriber extends Subscriber implements DownloadProgressListener { 21 | //弱引用结果回调 22 | private WeakReference mSubscriberOnNextListener; 23 | /*下载数据*/ 24 | private DownLoadInfo downInfo; 25 | 26 | 27 | public ProgressDownSubscriber(DownLoadInfo downInfo) { 28 | this.mSubscriberOnNextListener = new WeakReference<>(downInfo.listener); 29 | this.downInfo = downInfo; 30 | } 31 | 32 | /** 33 | * 订阅开始时调用 34 | * 显示ProgressDialog 35 | */ 36 | @Override 37 | public void onStart() { 38 | if (mSubscriberOnNextListener.get() != null) { 39 | mSubscriberOnNextListener.get().onLoadStart(); 40 | } 41 | downInfo.state = DownLoadManager.DownState.START; 42 | } 43 | 44 | /** 45 | * 完成,隐藏ProgressDialog 46 | */ 47 | @Override 48 | public void onCompleted() { 49 | if (mSubscriberOnNextListener.get() != null) { 50 | mSubscriberOnNextListener.get().onLoadComplete(); 51 | } 52 | downInfo.state = DownLoadManager.DownState.FINISH; 53 | } 54 | 55 | /** 56 | * 对错误进行统一处理 57 | * 隐藏ProgressDialog 58 | * 59 | * @param e 60 | */ 61 | @Override 62 | public void onError(Throwable e) { 63 | /*停止下载*/ 64 | DownLoadManager.getDownLoadManager().stopDown(downInfo); 65 | if (mSubscriberOnNextListener.get() != null) { 66 | mSubscriberOnNextListener.get().onLoadError(e); 67 | } 68 | downInfo.state = DownLoadManager.DownState.ERROR; 69 | } 70 | 71 | /** 72 | * 将onNext方法中的返回结果交给Activity或Fragment自己处理 73 | * 74 | * @param t 创建Subscriber时的泛型类型 75 | */ 76 | @Override 77 | public void onNext(T t) { 78 | if (mSubscriberOnNextListener.get() != null) { 79 | mSubscriberOnNextListener.get().onNext(t); 80 | } 81 | } 82 | 83 | @Override 84 | public void update(long read, long count, boolean done) { 85 | if (downInfo.countLength > count) { 86 | read = downInfo.countLength - count + read; 87 | } else { 88 | downInfo.countLength = count; 89 | } 90 | downInfo.readLength = read; 91 | if (mSubscriberOnNextListener.get() != null) { 92 | /*接受进度消息,造成UI阻塞,如果不需要显示进度可去掉实现逻辑,减少压力*/ 93 | rx.Observable.just(read).observeOn(AndroidSchedulers.mainThread()) 94 | .subscribe(new Action1() { 95 | @Override 96 | public void call(Long aLong) { 97 | /*如果暂停或者停止状态延迟,不需要继续发送回调,影响显示*/ 98 | if (downInfo.state == DownLoadManager.DownState.PAUSE || downInfo.state == DownLoadManager.DownState.STOP) 99 | return; 100 | downInfo.state = DownLoadManager.DownState.DOWN; 101 | mSubscriberOnNextListener.get().updateProgress(aLong, downInfo.countLength); 102 | } 103 | }); 104 | } 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/download/exception/HttpTimeException.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.download.exception; 2 | 3 | /** 4 | * @author xiaolong 5 | * @version v1.0 6 | * @function <描述功能> 7 | * @date 2017/2/10-9:51 8 | */ 9 | public class HttpTimeException extends RuntimeException { 10 | 11 | public static final int NO_DATA = 0x2; 12 | 13 | public HttpTimeException(int resultCode) { 14 | this(getApiExceptionMessage(resultCode)); 15 | } 16 | 17 | public HttpTimeException(String detailMessage) { 18 | super(detailMessage); 19 | } 20 | 21 | /** 22 | * 转换错误数据 23 | * 24 | * @param code 25 | * @return 26 | */ 27 | private static String getApiExceptionMessage(int code) { 28 | String message = ""; 29 | switch (code) { 30 | case NO_DATA: 31 | message = "无数据"; 32 | break; 33 | default: 34 | message = "error"; 35 | break; 36 | 37 | } 38 | return message; 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/download/exception/RetryWhenNetworkException.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.download.exception; 2 | 3 | import android.util.Log; 4 | 5 | import java.net.ConnectException; 6 | import java.net.SocketTimeoutException; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.concurrent.TimeoutException; 9 | 10 | import rx.Observable; 11 | import rx.functions.Func1; 12 | import rx.functions.Func2; 13 | 14 | /** 15 | * @author xiaolong 16 | * @version v1.0 17 | * @function <描述功能> 18 | */ 19 | public class RetryWhenNetworkException implements Func1, Observable> { 20 | // retry次数 21 | private int count = 3; 22 | // 延迟 23 | private long delay = 3000; 24 | // 叠加延迟 25 | private long increaseDelay = 3000; 26 | 27 | public RetryWhenNetworkException() { 28 | 29 | } 30 | 31 | public RetryWhenNetworkException(int count, long delay) { 32 | this.count = count; 33 | this.delay = delay; 34 | } 35 | 36 | public RetryWhenNetworkException(int count, long delay, long increaseDelay) { 37 | this.count = count; 38 | this.delay = delay; 39 | this.increaseDelay = increaseDelay; 40 | } 41 | 42 | @Override 43 | public Observable call(Observable observable) { 44 | return observable 45 | .zipWith(Observable.range(1, count + 1), new Func2() { 46 | @Override 47 | public Wrapper call(Throwable throwable, Integer integer) { 48 | return new Wrapper(throwable, integer); 49 | } 50 | }).flatMap((Func1>) wrapper -> { 51 | if ((wrapper.throwable instanceof ConnectException 52 | || wrapper.throwable instanceof SocketTimeoutException 53 | || wrapper.throwable instanceof TimeoutException) 54 | && wrapper.index < count + 1) { //如果超出重试次数也抛出错误,否则默认是会进入onCompleted 55 | Log.e("tag","retry---->"+wrapper.index); 56 | return Observable.timer(delay + (wrapper.index - 1) * increaseDelay, TimeUnit.MILLISECONDS); 57 | 58 | } 59 | return Observable.error(wrapper.throwable); 60 | }); 61 | } 62 | 63 | private class Wrapper { 64 | private int index; 65 | private Throwable throwable; 66 | 67 | public Wrapper(Throwable throwable, int index) { 68 | this.index = index; 69 | this.throwable = throwable; 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/CircleProgressBar.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.pdf; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.graphics.Rect; 9 | import android.graphics.RectF; 10 | import android.util.AttributeSet; 11 | import android.view.View; 12 | 13 | /** 14 | * @author xiaolong 15 | * @version v1.0 16 | * @function <描述功能> 17 | * @date 2017/2/10-11:02 18 | */ 19 | public class CircleProgressBar extends View { 20 | private int width;// 控件的宽度 21 | private int height;// 控件的高度 22 | private int radius;// 圆形的半径 23 | private int socktwidth = dp2px(8);// 圆环进度条的宽度 24 | private Paint paint = new Paint(); 25 | private Rect rec = new Rect(); 26 | private int value = 70;// 百分比0~100; 27 | private int textSize = dp2px(18);// 文字大小 28 | private Bitmap bitmap; 29 | @Deprecated 30 | float scale = 0.15f;// 中间背景图片相对圆环的大小的比例 31 | private int preColor = Color.parseColor("#2c2200");// 进度条未完成的颜色 32 | private int progressColor = Color.parseColor("#6bb849");// 进度条颜色 33 | private float paddingscale = 0.8f;// 控件内偏距占空间本身的比例 34 | private int CircleColor = Color.parseColor("#CCCCCC");// 圆中间的背景颜色 35 | private int textColor = progressColor;// 文字颜色 36 | private onProgressListener monProgress;// 进度时间监听 37 | private int startAngle = 270; 38 | RectF rectf = new RectF(); 39 | 40 | public CircleProgressBar(Context context, AttributeSet attrs) { 41 | super(context, attrs); 42 | } 43 | 44 | @Override 45 | protected void onDraw(Canvas canvas) { 46 | width = getWidth(); 47 | int size = height = getHeight(); 48 | if (height > width) 49 | size = width; 50 | radius = (int) (size * paddingscale / 2f); 51 | paint.setAntiAlias(true); 52 | paint.setColor(progressColor); 53 | // 绘制最大的圆 进度条圆环的背景颜色(未走到的进度)就是这个哦 54 | canvas.drawCircle(width / 2, height / 2, radius, paint); 55 | paint.setColor(preColor); 56 | canvas.drawCircle(width / 2, height / 2, radius-1, paint); 57 | rectf.set((width - radius * 2) / 2f, (height - radius * 2) / 2f, 58 | ((width - radius * 2) / 2f) + (2 * radius), 59 | ((height - radius * 2) / 2f) + (2 * radius)); 60 | paint.setColor(progressColor); 61 | canvas.drawArc(rectf, startAngle, value * 3.6f, true, paint); 62 | paint.setColor(CircleColor); 63 | // 绘制用于遮住伞形两个边的小圆 64 | canvas.drawCircle(width / 2, height / 2, radius - socktwidth, paint); 65 | if (bitmap != null) {// 绘制中间的图片 66 | int width2 = (int) (rectf.width() * scale); 67 | int height2 = (int) (rectf.height() * scale); 68 | rectf.set(rectf.left + width2, rectf.top + height2, rectf.right 69 | - width2, rectf.bottom - height2); 70 | canvas.drawBitmap(bitmap, null, rectf, null); 71 | } 72 | String v = value + "%"; 73 | paint.setColor(textColor); 74 | paint.setTextSize(textSize); 75 | paint.getTextBounds(v, 0, v.length(), rec); 76 | int textwidth = rec.width(); 77 | int textheight = rec.height(); 78 | // 绘制中间文字 79 | canvas.drawText(v, (width - textwidth) / 2, 80 | ((height + textheight) / 2), paint); 81 | super.onDraw(canvas); 82 | } 83 | 84 | public int dp2px(int dp) { 85 | return (int) ((getResources().getDisplayMetrics().density * dp) + 0.5); 86 | } 87 | 88 | /** 89 | * 设置进度 90 | * 91 | * @param value

92 | * ps: 百分比 0~100; 93 | */ 94 | public void setValue(int value) { 95 | if (value > 100) 96 | return; 97 | this.value = value; 98 | invalidate(); 99 | if (monProgress != null) 100 | monProgress.onProgress(value); 101 | } 102 | 103 | /** 104 | * 设置圆环进度条的宽度 px 105 | */ 106 | public CircleProgressBar setProdressWidth(int width) { 107 | this.socktwidth = width; 108 | return this; 109 | } 110 | 111 | /** 112 | * 设置文字大小 113 | * 114 | * @param value 115 | */ 116 | public CircleProgressBar setTextSize(int value) { 117 | textSize = value; 118 | return this; 119 | } 120 | 121 | /** 122 | * 设置文字大小 123 | */ 124 | public CircleProgressBar setTextColor(int color) { 125 | this.textColor = color; 126 | return this; 127 | } 128 | 129 | /** 130 | * 设置进度条之前的颜色 131 | */ 132 | public CircleProgressBar setPreProgress(int precolor) { 133 | this.preColor = precolor; 134 | return this; 135 | } 136 | 137 | /** 138 | * 设置进度颜色 139 | * 140 | * @param color 141 | */ 142 | public CircleProgressBar setProgress(int color) { 143 | this.progressColor = color; 144 | return this; 145 | } 146 | 147 | /** 148 | * 设置圆心中间的背景颜色 149 | * 150 | * @param color 151 | * @return 152 | */ 153 | public CircleProgressBar setCircleBackgroud(int color) { 154 | this.CircleColor = color; 155 | return this; 156 | } 157 | 158 | /** 159 | * 设置圆相对整个控件的宽度或者高度的占用比例 160 | * 161 | * @param scale 162 | */ 163 | public CircleProgressBar setPaddingscale(float scale) { 164 | this.paddingscale = scale; 165 | return this; 166 | } 167 | 168 | /** 169 | * 设置开始的位置 170 | * 171 | * @param startAngle 0~360 172 | *

173 | * ps 0代表在最右边 90 最下方 按照然后顺时针旋转 174 | */ 175 | public CircleProgressBar setStartAngle(int startAngle) { 176 | this.startAngle = startAngle; 177 | return this; 178 | } 179 | 180 | public interface onProgressListener { 181 | void onProgress(int value); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/PDFManager.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.pdf; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.net.Uri; 6 | import android.util.Log; 7 | 8 | import com.shockwave.pdfium.PdfDocument; 9 | import com.shockwave.pdfium.PdfiumCore; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.util.List; 15 | 16 | import cn.xiaolong.pdfiumpdfviewer.pdf.source.AssetSource; 17 | import cn.xiaolong.pdfiumpdfviewer.pdf.source.ByteArraySource; 18 | import cn.xiaolong.pdfiumpdfviewer.pdf.source.DocumentSource; 19 | import cn.xiaolong.pdfiumpdfviewer.pdf.source.FileSource; 20 | import cn.xiaolong.pdfiumpdfviewer.pdf.source.InputStreamSource; 21 | import cn.xiaolong.pdfiumpdfviewer.pdf.source.UriSource; 22 | import rx.Observable; 23 | import rx.schedulers.Schedulers; 24 | //参照着读取文件流的封装。如果想要把manager这个类变得可扩展, 25 | // 比如说后面我们添加了DocManager..等等图片生成的Manager 26 | //这个时候就应该把用到的方法抽出来,图片生成的方法,资源回收的方法等。 27 | // 获取总数的方法抽到某个interface/父类中去,通过继承还有接口实现来达到目的。 28 | // 到那时候就可以直接使用父类的Manager来指定自己需要实例化的对象。 29 | //这也正是封装,继承和多态的妙处 30 | 31 | /** 32 | * @author xiaolong 33 | * @version v1.0 34 | * @function <描述功能> 35 | * @date 2017/2/27-17:15 36 | */ 37 | public class PDFManager { 38 | private static String TAG = "PDF"; 39 | private Context mContext; 40 | private PdfiumCore mPdfiumCore; 41 | private PdfDocument mPdfDocument; 42 | private int pageCount; 43 | private int realWidth = 0; 44 | private int realHeight = 0; 45 | 46 | private PDFManager(Context context, DocumentSource documentSource, String password, 47 | OnOpenErrorListener onErrorListener, OnOpenSuccessListener onOpenSuccessListener) { 48 | mContext = context; 49 | mPdfiumCore = new PdfiumCore(context); 50 | try { 51 | mPdfDocument = documentSource.createDocument(context, mPdfiumCore, password); 52 | pageCount = mPdfiumCore.getPageCount(mPdfDocument); 53 | if (onOpenSuccessListener != null) 54 | onOpenSuccessListener.onSuccess(); 55 | } catch (IOException e) { 56 | e.printStackTrace(); 57 | Log.e(TAG, "PDF Couldn't Open"); 58 | if (onErrorListener != null) 59 | onErrorListener.onError(e.getCause()); 60 | } 61 | 62 | } 63 | 64 | public Observable getPdfBitmapNormalSize(final int pageIndex) { 65 | return Observable.create((Observable.OnSubscribe) subscriber -> { 66 | mPdfiumCore.openPage(mPdfDocument, pageIndex); 67 | if (realWidth == 0) { 68 | realWidth = mPdfiumCore.getPageWidthPoint(mPdfDocument, pageIndex); 69 | realHeight = mPdfiumCore.getPageHeightPoint(mPdfDocument, pageIndex); 70 | } 71 | Bitmap bitmap = Bitmap.createBitmap(realWidth * 2, realHeight * 2, 72 | Bitmap.Config.ARGB_8888); 73 | mPdfiumCore.renderPageBitmap(mPdfDocument, bitmap, pageIndex, 0, 0, 74 | realWidth, realWidth); 75 | subscriber.onNext(bitmap); 76 | subscriber.onCompleted(); 77 | }).subscribeOn(Schedulers.io()); 78 | 79 | 80 | // printInfo(mPdfiumCore, mPdfDocument); 81 | // return Observable.just(bitmap); 82 | } 83 | 84 | public Observable getPdfBitmapCustomSize(final int pageIndex, final int width) { 85 | return Observable.create((Observable.OnSubscribe) subscriber -> { 86 | mPdfiumCore.openPage(mPdfDocument, pageIndex); 87 | if (realWidth == 0) { 88 | realWidth = mPdfiumCore.getPageWidthPoint(mPdfDocument, pageIndex); 89 | realHeight = mPdfiumCore.getPageHeightPoint(mPdfDocument, pageIndex); 90 | } 91 | int height = (realHeight * width) / realWidth; 92 | Bitmap bitmap = Bitmap.createBitmap(width, height, 93 | Bitmap.Config.ARGB_8888); 94 | mPdfiumCore.renderPageBitmap(mPdfDocument, bitmap, pageIndex, 0, 0, 95 | width, height); 96 | subscriber.onNext(bitmap); 97 | subscriber.onCompleted(); 98 | }).subscribeOn(Schedulers.io()); 99 | 100 | // printInfo(mPdfiumCore, mPdfDocument); 101 | } 102 | 103 | public int pageCount() { 104 | return pageCount; 105 | } 106 | 107 | /** 108 | * this must do when all work have done, to recycle the memory 109 | */ 110 | public void recycle() { 111 | if (mPdfDocument != null) { 112 | mPdfiumCore.closeDocument(mPdfDocument); // important! 113 | } 114 | } 115 | 116 | public void printInfo(PdfiumCore core, PdfDocument doc) { 117 | PdfDocument.Meta meta = core.getDocumentMeta(doc); 118 | Log.e(TAG, "title = " + meta.getTitle()); 119 | Log.e(TAG, "author = " + meta.getAuthor()); 120 | Log.e(TAG, "subject = " + meta.getSubject()); 121 | Log.e(TAG, "keywords = " + meta.getKeywords()); 122 | Log.e(TAG, "creator = " + meta.getCreator()); 123 | Log.e(TAG, "producer = " + meta.getProducer()); 124 | Log.e(TAG, "creationDate = " + meta.getCreationDate()); 125 | Log.e(TAG, "modDate = " + meta.getModDate()); 126 | 127 | printBookmarksTree(core.getTableOfContents(doc), "-"); 128 | 129 | } 130 | 131 | public void printBookmarksTree(List tree, String sep) { 132 | for (PdfDocument.Bookmark b : tree) { 133 | 134 | Log.e(TAG, String.format("%s %s, p %d", sep, b.getTitle(), b.getPageIdx())); 135 | 136 | if (b.hasChildren()) { 137 | printBookmarksTree(b.getChildren(), sep + "-"); 138 | } 139 | } 140 | } 141 | 142 | public static class Builder { 143 | private DocumentSource mDocumentSource; 144 | private Context context; 145 | private String password; 146 | private OnOpenErrorListener mOnErrorListener; 147 | private OnOpenSuccessListener mOnOpenSuccessListener; 148 | private static final int KILOBYTE = 1024; 149 | private static final int BUFFER_LEN = 1 * KILOBYTE; 150 | private static final int NOTIFY_PERIOD = 150 * KILOBYTE; 151 | 152 | public Builder(Context context) { 153 | this.context = context; 154 | } 155 | 156 | public Builder pdfFromFile(File file) { 157 | mDocumentSource = new FileSource(file); 158 | return this; 159 | } 160 | 161 | public Builder pdfFromStream(InputStream inputStream) { 162 | mDocumentSource = new InputStreamSource(inputStream); 163 | return this; 164 | } 165 | 166 | public Builder pdfFromUri(Uri uri) { 167 | mDocumentSource = new UriSource(uri); 168 | return this; 169 | } 170 | 171 | public Builder pdfFormAsset(String assetName) { 172 | mDocumentSource = new AssetSource(assetName); 173 | return this; 174 | } 175 | 176 | public Builder pdfFromByte(byte[] data) { 177 | mDocumentSource = new ByteArraySource(data); 178 | return this; 179 | } 180 | 181 | public Builder setPassword(String password) { 182 | this.password = password; 183 | return this; 184 | } 185 | 186 | public Builder setOnOpenErrorListener(OnOpenErrorListener onErrorListener) { 187 | this.mOnErrorListener = onErrorListener; 188 | return this; 189 | } 190 | 191 | public Builder setOnOpenSuccessListener(OnOpenSuccessListener onOpenSuccessListener) { 192 | this.mOnOpenSuccessListener = onOpenSuccessListener; 193 | return this; 194 | } 195 | 196 | public PDFManager build() { 197 | return new PDFManager(context, mDocumentSource, password, mOnErrorListener, mOnOpenSuccessListener); 198 | } 199 | } 200 | 201 | public interface OnOpenErrorListener { 202 | /** 203 | * Called if error occurred while opening PDF 204 | * 205 | * @param t Throwable with error 206 | */ 207 | void onError(Throwable t); 208 | } 209 | 210 | public interface OnOpenSuccessListener { 211 | void onSuccess(); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/PhotoViewPager.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.pdf; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.ViewPager; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | 8 | /** 9 | * @author xiaolong 10 | * @version v1.0 11 | * @function 12 | * @date 2017/2/28-16:41 13 | */ 14 | public class PhotoViewPager extends ViewPager { 15 | public PhotoViewPager(Context context) { 16 | super(context); 17 | } 18 | 19 | public PhotoViewPager(Context context, AttributeSet attrs) { 20 | super(context, attrs); 21 | } 22 | 23 | @Override 24 | public boolean onInterceptTouchEvent(MotionEvent ev) { 25 | try { 26 | return super.onInterceptTouchEvent(ev); 27 | } catch (IllegalArgumentException e) { 28 | e.printStackTrace(); 29 | } catch (ArrayIndexOutOfBoundsException e) { 30 | e.printStackTrace(); 31 | } 32 | return false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/adapter/PdfGuideAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.pdf.adapter; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | 12 | import java.util.concurrent.ConcurrentHashMap; 13 | 14 | import cn.xiaolong.pdfiumpdfviewer.R; 15 | import cn.xiaolong.pdfiumpdfviewer.pdf.PDFManager; 16 | import cn.xiaolong.pdfiumpdfviewer.pdf.list.ViewHolder; 17 | import rx.Observable; 18 | import rx.Subscriber; 19 | import rx.android.schedulers.AndroidSchedulers; 20 | 21 | 22 | /** 23 | * @author xiaolong 24 | * @version v1.0 25 | * @function <左侧列表适配器> 26 | * @date 2017/2/27-14:50 27 | */ 28 | public class PdfGuideAdapter extends BaseAdapter { 29 | 30 | private PDFManager mPdfManager; 31 | private Context mContext; 32 | private ConcurrentHashMap mConcurrentHashMap; 33 | private View.OnClickListener mOnItemClickListener; 34 | private int[] mStates; 35 | private int count; 36 | 37 | public PdfGuideAdapter(Context context, PDFManager pdfManager) { 38 | this.mPdfManager = pdfManager; 39 | this.mContext = context; 40 | mConcurrentHashMap = new ConcurrentHashMap(); 41 | count = mPdfManager.pageCount(); 42 | mStates = new int[count]; 43 | } 44 | 45 | @Override 46 | public int getCount() { 47 | return mPdfManager.pageCount(); 48 | } 49 | 50 | @Override 51 | public Object getItem(int position) { 52 | // return mPdfManager.getPdfBitmapNormalSize(position); 53 | return "sb"; 54 | } 55 | 56 | public void setOnItemClickListener(View.OnClickListener onItemClickListener) { 57 | mOnItemClickListener = onItemClickListener; 58 | } 59 | 60 | @Override 61 | public long getItemId(int position) { 62 | return position; 63 | } 64 | 65 | @Override 66 | public View getView(final int position, View convertView, ViewGroup parent) { 67 | ViewHolder viewHolder = ViewHolder.get(mContext, convertView, parent, 68 | R.layout.listitem_guide, position); 69 | TextView tvGuideName = viewHolder.getView(R.id.tvLocate); 70 | final ImageView imageView = viewHolder.getView(R.id.imageView); 71 | getBitmap(position).observeOn(AndroidSchedulers.mainThread()).subscribeOn(AndroidSchedulers.mainThread()) 72 | .subscribe(new Subscriber() { 73 | @Override 74 | public void onStart() { 75 | super.onStart(); 76 | // imageView.setImageResource(R.drawable.ic_delete); 77 | } 78 | 79 | @Override 80 | public void onCompleted() { 81 | // Log.d("adapterloadCompleted","completed"); 82 | 83 | } 84 | 85 | @Override 86 | public void onError(Throwable e) { 87 | // Log.d("adapterError",e.getMessage()); 88 | } 89 | 90 | @Override 91 | public void onNext(Bitmap bitmap) { 92 | imageView.setImageBitmap(bitmap); 93 | } 94 | }); 95 | tvGuideName.setText(position + 1 + ""); 96 | if (mStates[position] == 1) { 97 | imageView.setSelected(true); 98 | } else { 99 | imageView.setSelected(false); 100 | } 101 | if (mOnItemClickListener != null) { 102 | imageView.setOnClickListener(v -> { 103 | v.setTag(position); 104 | mOnItemClickListener.onClick(v); 105 | viewHolder.getConvertView().setSelected(false); 106 | setStatePosition(position); 107 | }); 108 | } 109 | return viewHolder.getConvertView(); 110 | } 111 | 112 | public Observable getBitmap(final int position) { 113 | return mConcurrentHashMap.get(position) == null ? 114 | mPdfManager.getPdfBitmapCustomSize(position, 160) 115 | .doOnNext(bitmap -> mConcurrentHashMap.put(position, bitmap)) : Observable.just(mConcurrentHashMap.get(position)); 116 | } 117 | 118 | public void setStatePosition(int position) { 119 | mStates = new int[count]; 120 | mStates[position] = 1; 121 | notifyDataSetChanged(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/adapter/PdfImageAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.pdf.adapter; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.support.v4.view.PagerAdapter; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | 11 | import cn.xiaolong.pdfiumpdfviewer.R; 12 | import cn.xiaolong.pdfiumpdfviewer.pdf.PDFManager; 13 | import cn.xiaolong.pdfiumpdfviewer.pdf.utils.ScreenUtil; 14 | import rx.Subscriber; 15 | import rx.android.schedulers.AndroidSchedulers; 16 | import uk.co.senab.photoview.PhotoView; 17 | 18 | /** 19 | * @author xiaolong 20 | * @version v1.0 21 | * @function 22 | * @date 2016/7/18-16:57 23 | */ 24 | public class PdfImageAdapter extends PagerAdapter { 25 | private Context context; 26 | private PDFManager mPDFManager; 27 | 28 | public PdfImageAdapter(Context context, PDFManager pdfManager) { 29 | this.context = context; 30 | mPDFManager = pdfManager; 31 | } 32 | 33 | @Override 34 | public void destroyItem(ViewGroup container, int position, Object object) { 35 | container.removeView((View) object); 36 | Log.d("msg", "page" + (position + 1) + "destory"); 37 | } 38 | 39 | 40 | @Override 41 | public int getCount() { 42 | return mPDFManager.pageCount(); 43 | } 44 | 45 | 46 | @Override 47 | public Object instantiateItem(ViewGroup container, int position) { 48 | View contentView = View.inflate(context, R.layout.item_viewpage_image, null); 49 | final PhotoView ivLargeImage = (PhotoView) contentView.findViewById(R.id.ivLargeImage); 50 | mPDFManager.getPdfBitmapCustomSize(position, ScreenUtil.getScreenSize(context)[0] * 7 / 8) 51 | .observeOn(AndroidSchedulers.mainThread()) 52 | .subscribe(new Subscriber() { 53 | @Override 54 | public void onStart() { 55 | ivLargeImage.setImageResource(R.mipmap.ic_launcher); 56 | } 57 | 58 | @Override 59 | public void onCompleted() { 60 | // Log.d("adapterloadCompleted", "completed"); 61 | } 62 | 63 | @Override 64 | public void onError(Throwable e) { 65 | } 66 | 67 | @Override 68 | public void onNext(Bitmap bitmap) { 69 | ivLargeImage.setImageBitmap(bitmap); 70 | } 71 | }); 72 | container.addView(contentView); 73 | return contentView; 74 | } 75 | 76 | @Override 77 | public boolean isViewFromObject(View arg0, Object arg1) { 78 | return arg0 == arg1; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/list/ViewHolder.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.pdf.list; 2 | 3 | import android.content.Context; 4 | import android.util.SparseArray; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | public class ViewHolder 10 | { 11 | private final SparseArray mViews; 12 | private View mConvertView; 13 | 14 | private ViewHolder(Context context, ViewGroup parent, int layoutId, 15 | int position) 16 | { 17 | this.mViews = new SparseArray(); 18 | mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, 19 | false); 20 | //setTag 21 | mConvertView.setTag(this); 22 | } 23 | 24 | /** 25 | * 拿到一个ViewHolder对象 26 | * @param context 27 | * @param convertView 28 | * @param parent 29 | * @param layoutId 30 | * @param position 31 | * @return 32 | */ 33 | public static ViewHolder get(Context context, View convertView, 34 | ViewGroup parent, int layoutId, int position) 35 | { 36 | 37 | if (convertView == null) 38 | { 39 | return new ViewHolder(context, parent, layoutId, position); 40 | } 41 | return (ViewHolder) convertView.getTag(); 42 | } 43 | 44 | 45 | /** 46 | * 通过控件的Id获取对于的控件,如果没有则加入views 47 | * @param viewId 48 | * @return 49 | */ 50 | public T getView(int viewId) 51 | { 52 | 53 | View view = mViews.get(viewId); 54 | if (view == null) 55 | { 56 | view = mConvertView.findViewById(viewId); 57 | mViews.put(viewId, view); 58 | } 59 | return (T) view; 60 | } 61 | 62 | public View getConvertView() 63 | { 64 | return mConvertView; 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/source/AssetSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Bartosz Schiller. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cn.xiaolong.pdfiumpdfviewer.pdf.source; 17 | 18 | 19 | import android.content.Context; 20 | import android.os.ParcelFileDescriptor; 21 | 22 | import com.shockwave.pdfium.PdfDocument; 23 | import com.shockwave.pdfium.PdfiumCore; 24 | 25 | import java.io.File; 26 | import java.io.IOException; 27 | 28 | import cn.xiaolong.pdfiumpdfviewer.pdf.utils.FileUtils; 29 | 30 | public class AssetSource implements DocumentSource { 31 | 32 | private final String assetName; 33 | 34 | public AssetSource(String assetName) { 35 | this.assetName = assetName; 36 | } 37 | 38 | @Override 39 | public PdfDocument createDocument(Context context, PdfiumCore core, String password) throws IOException { 40 | File f = FileUtils.fileFromAsset(context, assetName); 41 | ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); 42 | return core.newDocument(pfd, password); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/source/ByteArraySource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Bartosz Schiller. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cn.xiaolong.pdfiumpdfviewer.pdf.source; 17 | 18 | import android.content.Context; 19 | 20 | import com.shockwave.pdfium.PdfDocument; 21 | import com.shockwave.pdfium.PdfiumCore; 22 | 23 | import java.io.IOException; 24 | 25 | public class ByteArraySource implements DocumentSource { 26 | 27 | private byte[] data; 28 | 29 | public ByteArraySource(byte[] data) { 30 | this.data = data; 31 | } 32 | 33 | @Override 34 | public PdfDocument createDocument(Context context, PdfiumCore core, String password) throws IOException { 35 | return core.newDocument(data, password); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/source/DocumentSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Bartosz Schiller. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cn.xiaolong.pdfiumpdfviewer.pdf.source; 17 | 18 | import android.content.Context; 19 | 20 | import com.shockwave.pdfium.PdfDocument; 21 | import com.shockwave.pdfium.PdfiumCore; 22 | 23 | import java.io.IOException; 24 | 25 | public interface DocumentSource { 26 | PdfDocument createDocument(Context context, PdfiumCore core, String password) throws IOException; 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/source/FileSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Bartosz Schiller. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cn.xiaolong.pdfiumpdfviewer.pdf.source; 17 | 18 | import android.content.Context; 19 | import android.os.ParcelFileDescriptor; 20 | 21 | import com.shockwave.pdfium.PdfDocument; 22 | import com.shockwave.pdfium.PdfiumCore; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | 27 | public class FileSource implements DocumentSource { 28 | 29 | private File file; 30 | 31 | public FileSource(File file) { 32 | this.file = file; 33 | } 34 | 35 | @Override 36 | public PdfDocument createDocument(Context context, PdfiumCore core, String password) throws IOException { 37 | ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); 38 | return core.newDocument(pfd, password); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/source/InputStreamSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Bartosz Schiller. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cn.xiaolong.pdfiumpdfviewer.pdf.source; 17 | 18 | import android.content.Context; 19 | 20 | import com.shockwave.pdfium.PdfDocument; 21 | import com.shockwave.pdfium.PdfiumCore; 22 | 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | 26 | import cn.xiaolong.pdfiumpdfviewer.pdf.utils.Util; 27 | 28 | public class InputStreamSource implements DocumentSource { 29 | 30 | private InputStream inputStream; 31 | 32 | @Override 33 | public PdfDocument createDocument(Context context, PdfiumCore core, String password) throws IOException { 34 | return core.newDocument(Util.toByteArray(inputStream), password); 35 | } 36 | 37 | public InputStreamSource(InputStream inputStream) { 38 | this.inputStream = inputStream; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/source/UriSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Bartosz Schiller. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cn.xiaolong.pdfiumpdfviewer.pdf.source; 17 | 18 | import android.content.Context; 19 | import android.net.Uri; 20 | import android.os.ParcelFileDescriptor; 21 | 22 | import com.shockwave.pdfium.PdfDocument; 23 | import com.shockwave.pdfium.PdfiumCore; 24 | 25 | import java.io.IOException; 26 | 27 | public class UriSource implements DocumentSource { 28 | 29 | private Uri uri; 30 | 31 | public UriSource(Uri uri) { 32 | this.uri = uri; 33 | } 34 | 35 | @Override 36 | public PdfDocument createDocument(Context context, PdfiumCore core, String password) throws IOException { 37 | ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r"); 38 | return core.newDocument(pfd, password); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/utils/DownloadFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Olmo Gallegos Hernández. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cn.xiaolong.pdfiumpdfviewer.pdf.utils; 17 | 18 | public interface DownloadFile { 19 | void download(String url, String destinationPath); 20 | void reload(); 21 | interface DownloadListener { 22 | void onSuccess(String url, String destinationPath); 23 | 24 | void onFailure(Exception e); 25 | 26 | void onProgressUpdate(int progress, int total); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/utils/DownloadFileUrlConnectionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Olmo Gallegos Hernández. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cn.xiaolong.pdfiumpdfviewer.pdf.utils; 17 | 18 | import android.content.Context; 19 | import android.os.Handler; 20 | import android.util.Log; 21 | 22 | import java.io.BufferedInputStream; 23 | import java.io.File; 24 | import java.io.FileOutputStream; 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.net.HttpURLConnection; 28 | import java.net.MalformedURLException; 29 | import java.net.URL; 30 | 31 | public class DownloadFileUrlConnectionImpl implements DownloadFile { 32 | private static final int KILOBYTE = 1024; 33 | 34 | private static final int BUFFER_LEN = 1 * KILOBYTE; 35 | private static final int NOTIFY_PERIOD = 150 * KILOBYTE; 36 | 37 | Context context; 38 | Handler uiThread; 39 | DownloadListener listener; 40 | private String mUrl = ""; 41 | private String mFilePath = ""; 42 | 43 | public DownloadFileUrlConnectionImpl(Context context, Handler uiThread, DownloadListener listener) { 44 | this.context = context; 45 | this.uiThread = uiThread; 46 | this.listener = listener; 47 | } 48 | 49 | @Override 50 | public void download(final String url, final String destinationPath) { 51 | if (mUrl.equals("") || mFilePath.equals("")) { 52 | this.mUrl = url; 53 | this.mFilePath = destinationPath; 54 | } 55 | new Thread(() -> { 56 | try { 57 | File file = new File(destinationPath); 58 | FileOutputStream fileOutput = new FileOutputStream(file); 59 | HttpURLConnection urlConnection = null; 60 | URL urlObj = new URL(url); 61 | urlConnection = (HttpURLConnection) urlObj.openConnection(); 62 | int totalSize = urlConnection.getContentLength(); 63 | int downloadedSize = 0; 64 | int counter = 0; 65 | byte[] buffer = new byte[BUFFER_LEN]; 66 | int bufferLength = 0; 67 | InputStream in = new BufferedInputStream(urlConnection.getInputStream()); 68 | 69 | while ((bufferLength = in.read(buffer)) > 0) { 70 | fileOutput.write(buffer, 0, bufferLength); 71 | downloadedSize += bufferLength; 72 | notifyProgressOnUiThread(downloadedSize, totalSize); 73 | } 74 | 75 | urlConnection.disconnect(); 76 | fileOutput.close(); 77 | notifySuccessOnUiThread(url, destinationPath); 78 | } catch (MalformedURLException e) { 79 | notifyFailureOnUiThread(e); 80 | } catch (IOException e) { 81 | notifyFailureOnUiThread(e); 82 | } 83 | 84 | }).start(); 85 | } 86 | 87 | @Override 88 | public void reload() { 89 | download(mUrl, mFilePath); 90 | } 91 | 92 | protected void notifySuccessOnUiThread(final String url, final String destinationPath) { 93 | if (uiThread == null) { 94 | return; 95 | } 96 | Log.e("DownLoad", "Success:" + url + " " + destinationPath); 97 | uiThread.post(() -> listener.onSuccess(url, destinationPath)); 98 | } 99 | 100 | protected void notifyFailureOnUiThread(final Exception e) { 101 | if (uiThread == null) { 102 | return; 103 | } 104 | Log.e("DownLoad", "Fail:" + e.toString()); 105 | uiThread.post(() -> listener.onFailure(e)); 106 | } 107 | 108 | private void notifyProgressOnUiThread(final int downloadedSize, final int totalSize) { 109 | if (uiThread == null) { 110 | return; 111 | } 112 | 113 | uiThread.post(() -> listener.onProgressUpdate(downloadedSize, totalSize)); 114 | } 115 | 116 | 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Bartosz Schiller 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cn.xiaolong.pdfiumpdfviewer.pdf.utils; 17 | 18 | import android.content.Context; 19 | import android.util.Log; 20 | 21 | import java.io.File; 22 | import java.io.FileInputStream; 23 | import java.io.FileNotFoundException; 24 | import java.io.FileOutputStream; 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.io.OutputStream; 28 | 29 | public class FileUtils { 30 | private static final int KILOBYTE = 1024; 31 | 32 | private static final int BUFFER_LEN = 1 * KILOBYTE; 33 | private static final int NOTIFY_PERIOD = 150 * KILOBYTE; 34 | 35 | private FileUtils() { 36 | // Prevents instantiation 37 | } 38 | 39 | public static File fileFromAsset(Context context, String assetName) throws IOException { 40 | File outFile = new File(context.getCacheDir(), assetName + "-pdfview.pdf"); 41 | if (assetName.contains("/")) { 42 | outFile.getParentFile().mkdirs(); 43 | } 44 | copy(context.getAssets().open(assetName), outFile); 45 | return outFile; 46 | } 47 | 48 | public static void copy(InputStream inputStream, File output) throws IOException { 49 | OutputStream outputStream = null; 50 | try { 51 | outputStream = new FileOutputStream(output); 52 | int read = 0; 53 | byte[] bytes = new byte[1024]; 54 | while ((read = inputStream.read(bytes)) != -1) { 55 | outputStream.write(bytes, 0, read); 56 | } 57 | } finally { 58 | try { 59 | if (inputStream != null) { 60 | inputStream.close(); 61 | } 62 | } finally { 63 | if (outputStream != null) { 64 | outputStream.close(); 65 | } 66 | } 67 | } 68 | } 69 | 70 | public static String extractFileNameFromURL(String url) { 71 | return url.substring(url.lastIndexOf('/') + 1); 72 | } 73 | 74 | /** 75 | * 获取指定文件大小 76 | * 77 | * @return 78 | * @throws Exception 79 | */ 80 | public static long getFileSize(File file) { 81 | long size = 0; 82 | try { 83 | if (file.exists()) { 84 | FileInputStream fis = null; 85 | fis = new FileInputStream(file); 86 | size = fis.available(); 87 | } else { 88 | file.createNewFile(); 89 | Log.e("获取文件大小", "文件不存在!"); 90 | } 91 | } catch (FileNotFoundException e) { 92 | e.printStackTrace(); 93 | } catch (IOException e) { 94 | e.printStackTrace(); 95 | } 96 | return size; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/utils/ScreenUtil.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer.pdf.utils; 2 | 3 | import android.content.Context; 4 | import android.util.DisplayMetrics; 5 | import android.view.Display; 6 | import android.view.WindowManager; 7 | 8 | /** 9 | * @author xiaolong 10 | * @version v1.0 11 | * @function <描述功能> 12 | * @date 2017/3/1-16:55 13 | */ 14 | public class ScreenUtil { 15 | 16 | public static int[] getScreenSize(Context context) { 17 | 18 | int[] size = new int[2]; 19 | 20 | WindowManager w = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 21 | Display d = w.getDefaultDisplay(); 22 | DisplayMetrics metrics = new DisplayMetrics(); 23 | d.getMetrics(metrics); 24 | // since SDK_INT = 1; 25 | int widthPixels = metrics.widthPixels; 26 | int heightPixels = metrics.heightPixels; 27 | size[0] = widthPixels; 28 | size[1] = heightPixels /*- getStatusBarHeight(context)*/; 29 | return size; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/cn/xiaolong/pdfiumpdfviewer/pdf/utils/Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Bartosz Schiller. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package cn.xiaolong.pdfiumpdfviewer.pdf.utils; 17 | 18 | import android.content.Context; 19 | import android.util.TypedValue; 20 | 21 | import java.io.ByteArrayOutputStream; 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | 25 | public class Util { 26 | private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; 27 | 28 | public static int getDP(Context context, int dp) { 29 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()); 30 | } 31 | 32 | public static byte[] toByteArray(InputStream inputStream) throws IOException { 33 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 34 | byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; 35 | int n; 36 | while (-1 != (n = inputStream.read(buffer))) { 37 | os.write(buffer, 0, n); 38 | } 39 | return os.toByteArray(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/drawable-xhdpi/ic_pack.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_unfolded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/drawable-xhdpi/ic_unfolded.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/corner6_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/corner_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/select_pdf_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/select_viewer_state.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/stroke_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | 28 | 29 | 41 | 42 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_viewpage_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/layout/listitem_guide.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 21 | 22 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #f2f4f5 7 | 8 | #666666 9 | #1f89ed 10 | 11 | 12 | #DBDBDB 13 | 14 | #333333 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | PDFiumPdfViewer 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/cn/xiaolong/pdfiumpdfviewer/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package cn.xiaolong.pdfiumpdfviewer; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.3' 9 | classpath 'me.tatarka:gradle-retrolambda:3.3.1' 10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' 11 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6' 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | jcenter() 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 08 17:23:14 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /lucky.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaolongonly/PDFiumForAndroidDemo/a73ad07515b566d7f3a0dbdf08c336b2eafcf2c1/lucky.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------