...
1
annotations/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
||||
15
annotations/build.gradle
Normal file
@ -0,0 +1,15 @@
|
||||
apply plugin: 'java'
|
||||
//apply plugin: 'com.novoda.bintray-release'
|
||||
|
||||
//publish {
|
||||
// userOrg = 'thefinestartist'
|
||||
// groupId = 'com.thefinestartist'
|
||||
// artifactId = 'annotations'
|
||||
// publishVersion = rootProject.ext.versionName
|
||||
// desc = 'Context free and basic utils to build Android project conveniently.'
|
||||
// website = 'https://github.com/TheFinestArtist/AndroidBaseUtils'
|
||||
//}
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
@ -102,6 +102,8 @@ dependencies {
|
||||
implementation("com.squareup.retrofit2:converter-scalars:2.6.4")
|
||||
implementation("androidx.viewpager2:viewpager2:1.0.0")
|
||||
implementation("com.squareup.picasso:picasso:2.71828")
|
||||
implementation("com.github.delight-im:Android-AdvancedWebView:v3.2.1")
|
||||
implementation(project(":library"))
|
||||
// implementation ("me.everything:providers-android:1.0.1")
|
||||
// implementation ("me.everything:providers-core:1.0.1")
|
||||
// implementation ("androidx.window:window:1.0.0")
|
||||
|
||||
@ -120,6 +120,8 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
||||
|
||||
<service
|
||||
android:name=".feeds.rss.RssService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
@ -151,9 +153,15 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name="com.wuadam.awesomewebview.AwesomeWebViewActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:hardwareAccelerated="true"
|
||||
android:theme="@style/FinestWebViewTheme.Light" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:authorities="bums.lunatic.launcher.fileprovider"
|
||||
android:exported="false"
|
||||
android:enabled="true"
|
||||
android:grantUriPermissions="true">
|
||||
|
||||
121
app/src/main/kotlin/bums/lunatic/launcher/home/RssViewer.kt
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Lunar Launcher
|
||||
* Copyright (C) 2022 Md Rasel Hossain
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bums.lunatic.launcher.home
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.print.PDFPrint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.net.toUri
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
||||
import bums.lunatic.launcher.R
|
||||
import bums.lunatic.launcher.databinding.RssViewerBinding
|
||||
import bums.lunatic.launcher.utils.BLog
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
internal class RssViewer : DialogFragment() {
|
||||
|
||||
private lateinit var binding: RssViewerBinding
|
||||
private lateinit var packageName: String
|
||||
private lateinit var packageManager: PackageManager
|
||||
private lateinit var defAppName: String
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, R.style.FilterFullScreenDialog)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (dialog != null) {
|
||||
// val metrics = resources.displayMetrics
|
||||
// val screenHeight = metrics.heightPixels
|
||||
// val bottomSheet =
|
||||
// dialog!!.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
|
||||
// bottomSheet.layoutParams.height = (screenHeight * 0.95).toInt()
|
||||
// val behavior = BottomSheetBehavior.from(bottomSheet)
|
||||
// behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
// behavior.skipCollapsed = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
binding = RssViewerBinding.inflate(inflater, container, false)
|
||||
binding.webview.loadUrl(tag!!)
|
||||
binding.webview.setDesktopMode(false)
|
||||
binding.pdfPrint.setOnClickListener { pdfPring() }
|
||||
return binding.root
|
||||
}
|
||||
|
||||
fun pdfPring() {
|
||||
val fileName = tag?.toUri()?.path?.replace("/","_")?.replace(".","_")
|
||||
val path = File(Environment.getExternalStorageDirectory(),"bums")
|
||||
if (path.exists() == false) {
|
||||
path.mkdirs()
|
||||
}
|
||||
val file = File(path, fileName.plus(".pdf"))
|
||||
|
||||
BLog.LOGE("file >>> ${file.absolutePath}")
|
||||
try {
|
||||
PDFPrint.generatePDFFromWebView(file,binding.webview, object : PDFPrint.OnPDFPrintListener {
|
||||
override fun onSuccess(file: File?) {
|
||||
BLog.LOGE("file >>>> ${file!!.absolutePath}")
|
||||
val shareIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
this.`package` = "com.synology.dsdrive"
|
||||
val imageUri = FileProvider.getUriForFile(
|
||||
lActivity!!,
|
||||
"bums.lunatic.launcher.fileprovider", //(use your app signature + ".provider" )
|
||||
file
|
||||
)
|
||||
putExtra(Intent.EXTRA_STREAM, imageUri)
|
||||
type = "pdf"
|
||||
}
|
||||
lActivity!!.startActivity(shareIntent)
|
||||
}
|
||||
|
||||
override fun onError(exception: java.lang.Exception?) {
|
||||
Toast.makeText(lActivity!!,
|
||||
"Pdf Save Failk ${exception?.localizedMessage}", Toast.LENGTH_LONG).show()
|
||||
exception?.printStackTrace()
|
||||
}
|
||||
} )
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
// (requireDialog() as BottomSheetDialog).dismissWithAnimation = true
|
||||
|
||||
}
|
||||
}
|
||||
@ -28,14 +28,13 @@ import androidx.core.net.toUri
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
|
||||
import bums.lunatic.launcher.R
|
||||
import bums.lunatic.launcher.databinding.ListItemWithBinding
|
||||
import bums.lunatic.launcher.home.RssViewer
|
||||
import bums.lunatic.launcher.model.RssData
|
||||
import bums.lunatic.launcher.model.RssDataInterface
|
||||
import bums.lunatic.launcher.model.RssDataType
|
||||
import bums.lunatic.launcher.openDotax
|
||||
import bums.lunatic.launcher.openNews
|
||||
import bums.lunatic.launcher.openOpera
|
||||
import bums.lunatic.launcher.openReddit
|
||||
import bums.lunatic.launcher.openYouTube
|
||||
import bums.lunatic.launcher.utils.BLog
|
||||
@ -43,12 +42,13 @@ import bums.lunatic.launcher.workers.WorkersDb
|
||||
import com.google.android.material.imageview.ShapeableImageView
|
||||
import com.google.gson.Gson
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.wuadam.awesomewebview.AwesomeWebView
|
||||
import io.realm.kotlin.UpdatePolicy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
internal class RssItemAdapter (
|
||||
|
||||
internal class RssItemAdapter (
|
||||
private val context: Context) : RecyclerView.Adapter<RssHolder>() {
|
||||
companion object {
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
@ -74,16 +74,39 @@ internal class RssItemAdapter (
|
||||
} else {
|
||||
if (RssDataType.REDDIT_NSFW.equals(rss.category())) {
|
||||
openReddit(rss.originPage())
|
||||
// RssViewer().apply {
|
||||
// show(lActivity!!.supportFragmentManager,rss.originPage)
|
||||
// }
|
||||
} else {
|
||||
openOpera(rss.originPage())
|
||||
RssViewer().apply {
|
||||
show(lActivity!!.supportFragmentManager,rss.originPage)
|
||||
}
|
||||
// openOpera(rss.originPage())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RssDataType.REDDIT -> { openReddit(rss.originPage()) }
|
||||
RssDataType.DOTAX -> { openDotax(rss.originPage()) }
|
||||
RssDataType.YOUTUBE -> { openYouTube(rss.originPage()) }
|
||||
else -> { openNews(rss.originPage()) }
|
||||
RssDataType.REDDIT -> {
|
||||
RssViewer().apply {
|
||||
show(lActivity!!.supportFragmentManager,rss.originPage)
|
||||
}
|
||||
// openReddit(rss.originPage())
|
||||
}
|
||||
RssDataType.DOTAX -> {
|
||||
RssViewer().apply {
|
||||
AwesomeWebView.Builder(lActivity!!).show(rss.originPage!!)
|
||||
// show(lActivity!!.supportFragmentManager,rss.originPage)
|
||||
}
|
||||
// openDotax(rss.originPage())
|
||||
}
|
||||
RssDataType.YOUTUBE -> { openYouTube(rss.originPage())
|
||||
}
|
||||
else -> {
|
||||
RssViewer().apply {
|
||||
show(lActivity!!.supportFragmentManager,rss.originPage)
|
||||
}
|
||||
// openNews(rss.originPage())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,12 +19,12 @@ object RssList {
|
||||
)
|
||||
val newsFeeds = arrayListOf(
|
||||
"https://news.google.com/rss?hl=ko&gl=KR&ceid=KR:ko",
|
||||
"https://imnews.imbc.com/rss/google_news/narrativeNews.rss",
|
||||
// "https://imnews.imbc.com/rss/google_news/narrativeNews.rss",
|
||||
"http://www.hani.co.kr/rss",
|
||||
"http://biz.heraldcorp.com/common_prog/rssdisp.php?ct=010000000000.xml",
|
||||
"https://rss.inews24.com/rss/news_inews.xml",
|
||||
"https://rss.nocutnews.co.kr/category/it.xml",
|
||||
"https://rss.nocutnews.co.kr/news/news.xml",
|
||||
// "https://rss.inews24.com/rss/news_inews.xml",
|
||||
// "https://rss.nocutnews.co.kr/category/it.xml",
|
||||
// "https://rss.nocutnews.co.kr/news/news.xml",
|
||||
"https://rss.nocutnews.co.kr/news/top.xml",
|
||||
)
|
||||
|
||||
|
||||
@ -26,12 +26,12 @@ class ArcaGetter : BaseGetter {
|
||||
temp.clear()
|
||||
val urls = arrayListOf(
|
||||
"https://arca.live/b/singbung?mode=best",
|
||||
// "https://arca.live/b/headline",
|
||||
"https://arca.live/b/headline",
|
||||
// "https://arca.live/b/live",
|
||||
"https://arca.live/b/namuhotnow",
|
||||
"https://arca.live/b/society",
|
||||
// "https://arca.live/b/replay",
|
||||
// "https://arca.live/b/breaking"
|
||||
"https://arca.live/b/breaking"
|
||||
)
|
||||
urls.forEach {
|
||||
Jsoup.connect(it)
|
||||
|
||||
@ -70,7 +70,7 @@ class ClienGetter : BaseGetter {
|
||||
RssDataType.CLIEN.isOn {
|
||||
try {
|
||||
temp.clear()
|
||||
val testUrl2 = arrayListOf("https://www.clien.net/service/group/community")
|
||||
val testUrl2 = arrayListOf("https://www.clien.net/service/group/community","https://www.clien.net/service/board/park","https://www.clien.net/service/board/news","https://www.clien.net/service/board/useful","https://www.clien.net/service/board/pds")
|
||||
testUrl2.forEach { url ->
|
||||
Jsoup.connect(url)
|
||||
.userAgent(USAGT)
|
||||
|
||||
@ -23,7 +23,7 @@ class DotaxGetter : BaseGetter {
|
||||
temp.clear()
|
||||
val dotaxUrls = arrayListOf("https://m.cafe.daum.net/dotax",
|
||||
"https://m.cafe.daum.net/dotax/_rec?page=2",
|
||||
"https://m.cafe.daum.net/dotax/_rec?page=3"
|
||||
// "https://m.cafe.daum.net/dotax/_rec?page=3"
|
||||
)
|
||||
dotaxUrls?.forEach {
|
||||
Jsoup.connect(it).userAgent(USAGT).get()?.let { dotax ->
|
||||
|
||||
@ -48,7 +48,7 @@ class TheQooGetter : BaseGetter {
|
||||
override fun realWork(): Result {
|
||||
RssDataType.THEQOO.isOn {
|
||||
try {
|
||||
val testUrl2 = arrayListOf("https://theqoo.net/hot")
|
||||
val testUrl2 = arrayListOf("https://theqoo.net/hot","https://theqoo.net/hot/category/512000937","https://theqoo.net/hot/category/24784","https://theqoo.net/hot/category/24788")
|
||||
testUrl2.forEach { url ->
|
||||
Jsoup.connect(url)
|
||||
.userAgent(USAGT)
|
||||
|
||||
@ -85,7 +85,9 @@ object WorkersDb {
|
||||
try {
|
||||
getRealm().writeBlocking {
|
||||
try {
|
||||
this.copyToRealm(it, UpdatePolicy.ERROR)
|
||||
if(query<RssData>("chosung == $0",it.chosung).find().size == 0) {
|
||||
this.copyToRealm(it, UpdatePolicy.ERROR)
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
|
||||
}
|
||||
|
||||
@ -62,21 +62,21 @@
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:id="@+id/calendarDateView"
|
||||
android:background="#ddd000"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
android:orientation="vertical"
|
||||
app:spanCount="7"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="bums.lunatic.launcher.behavior.behaviorimpl.BehaviorRecT"
|
||||
/>
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:id="@+id/calendarDateView"
|
||||
android:background="#ddd000"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
android:orientation="vertical"
|
||||
app:spanCount="7"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="bums.lunatic.launcher.behavior.behaviorimpl.BehaviorRecT"
|
||||
/>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_main"
|
||||
|
||||
@ -228,12 +228,16 @@
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/base_bg"
|
||||
android:scrollbars="none"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="2"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
/>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="2"
|
||||
android:layout_margin="@dimen/default_layout_margin"
|
||||
android:id="@+id/smsList"
|
||||
android:layout_width="0dp"
|
||||
@ -251,6 +255,7 @@
|
||||
/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
android:layout_margin="@dimen/default_layout_margin"
|
||||
android:id="@+id/infoList"
|
||||
android:layout_width="0dp"
|
||||
@ -269,6 +274,7 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_margin="@dimen/default_layout_margin"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
android:id="@+id/notiList"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
|
||||
26
app/src/main/res/layout/rss_viewer.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:background="#22000000"
|
||||
android:padding="@dimen/default_padding"
|
||||
android:clickable="true"
|
||||
android:focusableInTouchMode="true">
|
||||
|
||||
<im.delight.android.webview.AdvancedWebView
|
||||
android:layout_margin="@dimen/default_layout_margin"
|
||||
android:id="@+id/webview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
<ImageButton
|
||||
app:layout_constraintTop_toBottomOf="@id/webview"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:id="@+id/pdf_print"
|
||||
android:src="@drawable/ic_down"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -88,4 +88,11 @@
|
||||
<item name="fontFamily">sans-serif-light</item>
|
||||
</style>
|
||||
|
||||
<style name="FilterFullScreenDialog" parent="Theme.AppCompat.Dialog">
|
||||
<item name="android:windowIsFloating">false</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:statusBarColor">#22000000</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@ -17,6 +17,24 @@ plugins {
|
||||
tasks.register<Delete>("clean") {
|
||||
delete(rootProject.buildDir)
|
||||
}
|
||||
|
||||
|
||||
ext {
|
||||
// minSdkVersion = 7
|
||||
// targetSdkVersion = 23
|
||||
// compileSdkVersion = 23
|
||||
// buildToolsVersion = '23.0.2'
|
||||
//
|
||||
// sourceCompatibility = JavaVersion.VERSION_1_7
|
||||
// targetCompatibility = JavaVersion.VERSION_1_7
|
||||
//
|
||||
// versionCode = 1
|
||||
// versionName = '0.9.5'
|
||||
//
|
||||
// supportLibVersion = '23.3.0'
|
||||
// playLibVersion = '8.4.0'
|
||||
}
|
||||
|
||||
//repositories {
|
||||
// mavenCentral()
|
||||
// maven {
|
||||
|
||||
@ -24,3 +24,4 @@ android.useAndroidX=true
|
||||
android.nonTransitiveRClass=true
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonFinalResIds=true
|
||||
android.enableJetifier=true
|
||||
2
library/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build
|
||||
*.iml
|
||||
42
library/build.gradle
Normal file
@ -0,0 +1,42 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply from: 'maven_publish.gradle'
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk 14
|
||||
//noinspection ExpiredTargetSdkVersion
|
||||
targetSdk 31
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
}
|
||||
namespace 'com.wuadam.awesomewebview'
|
||||
lint {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
// https://developer.android.com/build/publish-library/configure-pub-variants?hl=zh-cn#single-pub-var
|
||||
// https://stackoverflow.com/a/71366104
|
||||
publishing {
|
||||
singleVariant('release') {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.annotation:annotation:1.7.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.10.0'
|
||||
implementation 'com.nineoldandroids:library:2.4.0'
|
||||
implementation project(':utils')
|
||||
// implementation("com.thefinestartist:utils:0.9.5")
|
||||
|
||||
|
||||
// api ('com.yanzhenjie:permission:2.0.3') {
|
||||
// exclude group: 'com.android.support'
|
||||
// }
|
||||
}
|
||||
16
library/maven_publish.gradle
Normal file
@ -0,0 +1,16 @@
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
// https://developer.android.com/build/publish-library/upload-library?hl=zh-cn#create-pub
|
||||
publishing {
|
||||
publications {
|
||||
release(MavenPublication) {
|
||||
groupId = 'com.github.hzw1199'
|
||||
artifactId = 'AwesomeWebView-Android'
|
||||
version = '2.1.0'
|
||||
|
||||
afterEvaluate {
|
||||
from components.release
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
library/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/Leonardo/Library/Android/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# AndPermission
|
||||
-dontwarn com.yanzhenjie.permission.**
|
||||
|
||||
# JavascriptInterface
|
||||
-keepclassmembers class com.wuadam.awesomewebview.helpers.VideoJsHelper$JavascriptInterface {
|
||||
public *;
|
||||
}
|
||||
14
library/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<provider
|
||||
android:name=".helpers.FileProvider4WebView"
|
||||
android:authorities="${applicationId}.awesome_web_view.file_provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
1016
library/src/main/java/com/wuadam/awesomewebview/AwesomeWebView.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.wuadam.awesomewebview.enums;
|
||||
|
||||
/**
|
||||
* Created by Leonardo on 11/14/15.
|
||||
*/
|
||||
public enum Position {
|
||||
TOP_OF_TOOLBAR,
|
||||
BOTTOM_OF_TOOLBAR,
|
||||
TOP_OF_WEBVIEW,
|
||||
BOTTOM_OF_WEBVIEW;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.wuadam.awesomewebview.helpers;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
|
||||
public class Base64ImgHelper {
|
||||
private String src;
|
||||
|
||||
public Base64ImgHelper(String src) {
|
||||
this.src = src;
|
||||
}
|
||||
|
||||
public boolean isBase64Img() {
|
||||
if (!TextUtils.isEmpty(src) && src.startsWith("data:image")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public byte[] decode() {
|
||||
src = src.substring(src.indexOf(","));
|
||||
byte[] imageAsBytes = Base64.decode(src.getBytes(), 0);
|
||||
return imageAsBytes;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package com.wuadam.awesomewebview.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Created by Leonardo on 11/21/15.
|
||||
*/
|
||||
public class BitmapHelper {
|
||||
|
||||
private BitmapHelper() {
|
||||
}
|
||||
|
||||
public static Bitmap getColoredBitmap(@NonNull Bitmap bitmap, @ColorInt int color) {
|
||||
int alpha = Color.alpha(color);
|
||||
int red = Color.red(color);
|
||||
int green = Color.green(color);
|
||||
int blue = Color.blue(color);
|
||||
|
||||
int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
|
||||
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
int pixel = pixels[i];
|
||||
int pixelAlpha = Color.alpha(pixel);
|
||||
if (pixelAlpha != 0) {
|
||||
pixels[i] = Color.argb((int) (pixelAlpha * alpha / 256f), red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap coloredBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
|
||||
coloredBitmap.setPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(),
|
||||
bitmap.getHeight());
|
||||
return coloredBitmap;
|
||||
}
|
||||
|
||||
public static Bitmap getColoredBitmap(@NonNull Context context, @DrawableRes int drawableRes,
|
||||
@ColorInt int color) {
|
||||
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), drawableRes);
|
||||
return getColoredBitmap(bitmap, color);
|
||||
}
|
||||
|
||||
public static Bitmap getGradientBitmap(int width, int height, @ColorInt int color) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
int alpha = Color.alpha(color);
|
||||
int red = Color.red(color);
|
||||
int green = Color.green(color);
|
||||
int blue = Color.blue(color);
|
||||
|
||||
int[] pixels = new int[width * height];
|
||||
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
|
||||
for (int y = 0; y < height; y++) {
|
||||
int gradientAlpha = (int) ((float) alpha * (float) (height - y) * (float) (height - y)
|
||||
/ (float) height
|
||||
/ (float) height);
|
||||
for (int x = 0; x < width; x++) {
|
||||
pixels[x + y * width] = Color.argb(gradientAlpha, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
bitmap.setPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.wuadam.awesomewebview.helpers;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
/**
|
||||
* Created by Leonardo on 11/28/15.
|
||||
*/
|
||||
public class ColorHelper {
|
||||
|
||||
private ColorHelper() {
|
||||
}
|
||||
|
||||
public static int disableColor(int color) {
|
||||
int alpha = Color.alpha(color);
|
||||
int red = Color.red(color);
|
||||
int green = Color.green(color);
|
||||
int blue = Color.blue(color);
|
||||
|
||||
return Color.argb((int) (alpha * 0.2f), red, green, blue);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,248 @@
|
||||
package com.wuadam.awesomewebview.helpers;
|
||||
|
||||
/**
|
||||
* Created by wuzongheng on 2018/2/18.
|
||||
*/
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* 图片下载的工具类
|
||||
*/
|
||||
public class DownPicUtil {
|
||||
|
||||
/**
|
||||
* Download the pic
|
||||
* @param url
|
||||
*/
|
||||
public static void downPic(String url, DownFinishListener downFinishListener){
|
||||
downPic(url, null, null, null, downFinishListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the pic
|
||||
* @param url
|
||||
*/
|
||||
public static void downPic(String url, String userAgent, String referer, String cookie, DownFinishListener downFinishListener){
|
||||
// 获取存储卡的目录
|
||||
String filePath = Environment.getExternalStorageDirectory().getPath();
|
||||
File file = new File(filePath + File.separator + Environment.DIRECTORY_DOWNLOADS);
|
||||
if(!file.exists()){
|
||||
file.mkdirs();
|
||||
}
|
||||
|
||||
loadPic(file.getPath(), url, userAgent, referer, cookie, downFinishListener);
|
||||
|
||||
}
|
||||
|
||||
private static void loadPic(final String filePath, final String url, final String userAgent, final String referer, final String cookie, final DownFinishListener downFinishListener) {
|
||||
new AsyncTask<Void,Void,String>(){
|
||||
String fileName;
|
||||
InputStream is;
|
||||
OutputStream out;
|
||||
|
||||
@Override
|
||||
protected String doInBackground(Void... voids) {
|
||||
|
||||
// 原文件名
|
||||
String[] split = url.split("/");
|
||||
fileName = split[split.length - 1];
|
||||
|
||||
// 创建目标文件,使用时间戳作为临时文件名,确保可以不重复
|
||||
String now = String.valueOf(System.currentTimeMillis());
|
||||
File picFile = new File(filePath + File.separator + now);
|
||||
if(! picFile.exists()) {
|
||||
// if file exists, do not download again
|
||||
try {
|
||||
Base64ImgHelper base64ImgHelper = new Base64ImgHelper(url);
|
||||
if (base64ImgHelper.isBase64Img()) {
|
||||
fileName = now;
|
||||
byte[] image = base64ImgHelper.decode();
|
||||
is = new ByteArrayInputStream(image); //处理服务器的响应结果
|
||||
} else {
|
||||
URL picUrl = new URL(url);
|
||||
//通过图片的链接打开输入流
|
||||
HttpURLConnection httpURLConnection = (HttpURLConnection) picUrl.openConnection();
|
||||
httpURLConnection.setConnectTimeout(10000); //设置连接超时时间
|
||||
httpURLConnection.setReadTimeout(30000);
|
||||
httpURLConnection.setDoInput(true); //打开输入流,以便从服务器获取数据
|
||||
httpURLConnection.setDoOutput(false); //Get请求不需要DoOutPut
|
||||
httpURLConnection.setRequestMethod("GET"); //设置以Get方式请求数据
|
||||
httpURLConnection.setUseCaches(false); //不使用缓存
|
||||
//设置请求体的类型是文本类型
|
||||
httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
if (!TextUtils.isEmpty(userAgent)) {
|
||||
httpURLConnection.setRequestProperty("User-Agent", userAgent);
|
||||
}
|
||||
if (!TextUtils.isEmpty(referer)) {
|
||||
httpURLConnection.setRequestProperty("Referer", referer);
|
||||
}
|
||||
if (!TextUtils.isEmpty(cookie)) {
|
||||
httpURLConnection.setRequestProperty("Cookie", cookie);
|
||||
}
|
||||
httpURLConnection.connect();
|
||||
|
||||
int response = httpURLConnection.getResponseCode(); //获得服务器的响应码
|
||||
if (response == HttpURLConnection.HTTP_OK || response == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
||||
is = httpURLConnection.getInputStream(); //处理服务器的响应结果
|
||||
if (is == null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
out = new FileOutputStream(picFile);
|
||||
byte[] b = new byte[1024];
|
||||
int end;
|
||||
while ((end = is.read(b)) != -1) {
|
||||
out.write(b, 0, end);
|
||||
}
|
||||
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 提取文件格式真实拓展名
|
||||
String extension = FormatHelper.getExtension(picFile);
|
||||
|
||||
// 提取不包含拓展名的原文件名
|
||||
String[] extensions = fileName.split("\\.");
|
||||
int splitLength = extensions.length;
|
||||
String newFileNameNoExtension;
|
||||
if (splitLength > 1) {
|
||||
newFileNameNoExtension = fileName.substring(0, fileName.length() - extensions[splitLength - 1].length() - 1);
|
||||
} else {
|
||||
newFileNameNoExtension = fileName;
|
||||
}
|
||||
|
||||
// 重命名文件
|
||||
if (extension == null) {
|
||||
// 不支持解析的格式,使用原文件名、原拓展名
|
||||
if (splitLength > 1) {
|
||||
// 有拓展名,在原文件名基础上递增重命名
|
||||
return renamePic(picFile, filePath, newFileNameNoExtension, extensions[splitLength - 1], MODE.MODE_INCREMENT);
|
||||
} else {
|
||||
// 无拓展名,整个文件名递增重命名
|
||||
return renamePic(picFile, filePath, newFileNameNoExtension, null, MODE.MODE_INCREMENT);
|
||||
}
|
||||
}
|
||||
// 支持解析的格式,使用md5文件名、真实拓展名
|
||||
String md5 = Md5Helper.getFileMD5ToString(picFile);
|
||||
if (TextUtils.isEmpty(md5)) {
|
||||
return renamePic(picFile, filePath, newFileNameNoExtension, extension, MODE.MODE_INCREMENT);
|
||||
} else {
|
||||
return renamePic(picFile, filePath, md5.substring(0, 16), extension, MODE.MODE_IGNORE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String path) {
|
||||
super.onPostExecute(path);
|
||||
if(path!=null){
|
||||
downFinishListener.onDownFinish(path);
|
||||
} else {
|
||||
downFinishListener.onError();
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private enum MODE{
|
||||
/**
|
||||
* 已有文件名一致的文件,文件名后加上地增量(n)
|
||||
*/
|
||||
MODE_INCREMENT,
|
||||
/**
|
||||
* 已有文件名一致的文件,不执行
|
||||
*/
|
||||
MODE_IGNORE,
|
||||
/**
|
||||
* 已有文件名一致的文件,覆盖
|
||||
*/
|
||||
MODE_OVERRIDE
|
||||
}
|
||||
|
||||
private static String renamePic(File picFile, String filePath, String newFileNameNoExtension, String extension, MODE mode) {
|
||||
String extensionWithPoint = TextUtils.isEmpty(extension)? "": "." + extension;
|
||||
String newFileName = newFileNameNoExtension + extensionWithPoint;
|
||||
File newFile = new File(filePath + File.separator + newFileName);
|
||||
switch (mode) {
|
||||
|
||||
case MODE_INCREMENT:
|
||||
int count = 1;
|
||||
while (newFile.exists()) {
|
||||
// if file of new name exists, add (count) in filename;
|
||||
newFileName = newFileNameNoExtension + "(" + count + ")" + extensionWithPoint;
|
||||
newFile = new File(filePath + File.separator + newFileName);
|
||||
count ++;
|
||||
}
|
||||
break;
|
||||
case MODE_IGNORE:
|
||||
if (newFile.exists()) {
|
||||
return newFile.getPath();
|
||||
}
|
||||
break;
|
||||
case MODE_OVERRIDE:
|
||||
if (newFile.exists()) {
|
||||
if (!newFile.delete()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (rename(picFile, newFileName)) {
|
||||
return newFile.getPath();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean rename(final File file, final String newName) {
|
||||
// file is null then return false
|
||||
if (file == null) return false;
|
||||
// file doesn't exist then return false
|
||||
if (!file.exists()) return false;
|
||||
// the new name is space then return false
|
||||
if (TextUtils.isEmpty(newName)) return false;
|
||||
// the new name equals old name then return true
|
||||
if (newName.equals(file.getName())) return true;
|
||||
File newFile = new File(file.getParent() + File.separator + newName);
|
||||
// the new name of file exists then return false
|
||||
return !newFile.exists()
|
||||
&& file.renameTo(newFile);
|
||||
}
|
||||
|
||||
|
||||
//下载完成回调的接口
|
||||
public interface DownFinishListener{
|
||||
void onDownFinish(String path);
|
||||
void onError();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.wuadam.awesomewebview.helpers;
|
||||
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
@Keep
|
||||
public final class FileProvider4WebView extends FileProvider {
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
package com.wuadam.awesomewebview.helpers;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
public class FormatHelper {
|
||||
|
||||
private FormatHelper() {
|
||||
}
|
||||
|
||||
public static String getExtension(File file) {
|
||||
try {
|
||||
// long fileLength = file.length();
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
|
||||
bufferedInputStream.mark(Integer.MAX_VALUE);
|
||||
byte[] start;
|
||||
byte[] end;
|
||||
|
||||
// https://en.wikipedia.org/wiki/JPEG
|
||||
start = new byte[2];
|
||||
// end = new byte[2];
|
||||
bufferedInputStream.read(start);
|
||||
// bufferedInputStream.skip(fileLength - start.length - end.length);
|
||||
// bufferedInputStream.read(end);
|
||||
if (start[0] == (byte) 0xFF &&
|
||||
start[1] == (byte) 0xD8
|
||||
// &&
|
||||
// end[0] == (byte) 0xFF &&
|
||||
// end[1] == (byte) 0xD9
|
||||
) {
|
||||
return "jpg";
|
||||
}
|
||||
bufferedInputStream.reset();
|
||||
|
||||
// https://en.wikipedia.org/wiki/Portable_Network_Graphics
|
||||
start = new byte[8];
|
||||
bufferedInputStream.read(start);
|
||||
if (start[0] == (byte) 0x89 &&
|
||||
start[1] == (byte) 0x50 &&
|
||||
start[2] == (byte) 0x4E &&
|
||||
start[3] == (byte) 0x47 &&
|
||||
start[4] == (byte) 0x0D &&
|
||||
start[5] == (byte) 0x0A &&
|
||||
start[6] == (byte) 0x1A &&
|
||||
start[7] == (byte) 0x0A) {
|
||||
return "png";
|
||||
}
|
||||
bufferedInputStream.reset();
|
||||
|
||||
// https://developers.google.com/speed/webp/docs/riff_container
|
||||
// https://en.wikipedia.org/wiki/WebP
|
||||
start = new byte[4];
|
||||
end = new byte[4];
|
||||
bufferedInputStream.read(start);
|
||||
bufferedInputStream.skip(4);
|
||||
bufferedInputStream.read(end);
|
||||
if (start[0] == (byte) 0x52 &&
|
||||
start[1] == (byte) 0x49 &&
|
||||
start[2] == (byte) 0x46 &&
|
||||
start[3] == (byte) 0x46 &&
|
||||
end[0] == (byte) 0x57 &&
|
||||
end[1] == (byte) 0x45 &&
|
||||
end[2] == (byte) 0x42 &&
|
||||
end[3] == (byte) 0x50) {
|
||||
return "webp";
|
||||
}
|
||||
bufferedInputStream.reset();
|
||||
|
||||
// https://en.wikipedia.org/wiki/File:Empty_GIF_hex.png
|
||||
start = new byte[6];
|
||||
bufferedInputStream.read(start);
|
||||
if (start[0] == (byte) 0x47 &&
|
||||
start[1] == (byte) 0x49 &&
|
||||
start[2] == (byte) 0x46 &&
|
||||
start[3] == (byte) 0x38 &&
|
||||
start[4] == (byte) 0x39 &&
|
||||
start[5] == (byte) 0x61) {
|
||||
return "gif";
|
||||
}
|
||||
|
||||
bufferedInputStream.close();
|
||||
fileInputStream.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package com.wuadam.awesomewebview.helpers;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class Md5Helper {
|
||||
|
||||
/**
|
||||
* Return the MD5 of file.
|
||||
*
|
||||
* @param file The file.
|
||||
* @return the md5 of file
|
||||
*/
|
||||
public static String getFileMD5ToString(final File file) {
|
||||
return bytes2HexString(getFileMD5(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MD5 of file.
|
||||
*
|
||||
* @param file The file.
|
||||
* @return the md5 of file
|
||||
*/
|
||||
public static byte[] getFileMD5(final File file) {
|
||||
if (file == null) return null;
|
||||
DigestInputStream dis = null;
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
dis = new DigestInputStream(fis, md);
|
||||
byte[] buffer = new byte[1024 * 256];
|
||||
while (true) {
|
||||
if (!(dis.read(buffer) > 0)) break;
|
||||
}
|
||||
md = dis.getMessageDigest();
|
||||
return md.digest();
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (dis != null) {
|
||||
dis.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final char[] HEX_DIGITS =
|
||||
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
|
||||
private static String bytes2HexString(final byte[] bytes) {
|
||||
if (bytes == null) return "";
|
||||
int len = bytes.length;
|
||||
if (len <= 0) return "";
|
||||
char[] ret = new char[len << 1];
|
||||
for (int i = 0, j = 0; i < len; i++) {
|
||||
ret[j++] = HEX_DIGITS[bytes[i] >> 4 & 0x0f];
|
||||
ret[j++] = HEX_DIGITS[bytes[i] & 0x0f];
|
||||
}
|
||||
return new String(ret);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package com.wuadam.awesomewebview.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
//import com.yanzhenjie.permission.Action;
|
||||
//import com.yanzhenjie.permission.AndPermission;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by wuzongheng on 2016/11/5.
|
||||
*/
|
||||
|
||||
public class PermissionHelper {
|
||||
|
||||
public static boolean hasPermissions(Context context, String... permissionName) {
|
||||
for (String permission : permissionName) {
|
||||
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<String> getGrantedPermissions(Context context, String... permissionName) {
|
||||
List<String> permissionsGranted = new ArrayList<>();
|
||||
for (String permission : permissionName) {
|
||||
if (ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) {
|
||||
permissionsGranted.add(permission);
|
||||
}
|
||||
}
|
||||
return permissionsGranted;
|
||||
}
|
||||
|
||||
public interface CheckPermissionListener {
|
||||
void onAllGranted(boolean sync);
|
||||
|
||||
/**
|
||||
* Partly granted(deniedPermissions.size() >= 0) or all denied
|
||||
*/
|
||||
void onPartlyGranted(List<String> permissionsDenied, boolean sync);
|
||||
}
|
||||
|
||||
public static void CheckPermissions(final Context context, final CheckPermissionListener checkPermissionListener, String... permissionName) {
|
||||
|
||||
if (hasPermissions(context, permissionName)) {
|
||||
if (checkPermissionListener != null) {
|
||||
checkPermissionListener.onAllGranted(true);
|
||||
}
|
||||
}else {
|
||||
// AndPermission.with(context)
|
||||
// .runtime()
|
||||
// .permission(permissionName)
|
||||
// .onGranted(new Action<List<String>>() {
|
||||
// @Override
|
||||
// public void onAction(List<String> permissions) {
|
||||
// // 权限申请成功回调。
|
||||
// if (checkPermissionListener != null) {
|
||||
// checkPermissionListener.onAllGranted(false);
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .onDenied(new Action<List<String>>() {
|
||||
// @Override
|
||||
// public void onAction(List<String> permissions) {
|
||||
// if (checkPermissionListener != null) {
|
||||
// checkPermissionListener.onPartlyGranted(permissions, false);
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .start();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.wuadam.awesomewebview.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import androidx.collection.SimpleArrayMap;
|
||||
|
||||
/**
|
||||
* Created by Leonardo on 11/14/15.
|
||||
*/
|
||||
|
||||
/*
|
||||
Each call to Typeface.createFromAsset will load a new instance of the typeface into memory,
|
||||
and this memory is not consistently get garbage collected
|
||||
http://code.google.com/p/android/issues/detail?id=9904
|
||||
(It states released but even on Lollipop you can see the typefaces accumulate even after
|
||||
multiple GC passes)
|
||||
You can detect this by running:
|
||||
adb shell dumpsys meminfo com.your.packagenage
|
||||
You will see output like:
|
||||
Asset Allocations
|
||||
zip:/data/app/com.your.packagenage-1.apk:/assets/Roboto-Medium.ttf: 125K
|
||||
zip:/data/app/com.your.packagenage-1.apk:/assets/Roboto-Medium.ttf: 125K
|
||||
zip:/data/app/com.your.packagenage-1.apk:/assets/Roboto-Medium.ttf: 125K
|
||||
zip:/data/app/com.your.packagenage-1.apk:/assets/Roboto-Regular.ttf: 123K
|
||||
zip:/data/app/com.your.packagenage-1.apk:/assets/Roboto-Medium.ttf: 125K
|
||||
*/
|
||||
public class TypefaceHelper {
|
||||
|
||||
private static final SimpleArrayMap<String, Typeface> cache = new SimpleArrayMap<>();
|
||||
|
||||
private TypefaceHelper() {
|
||||
}
|
||||
|
||||
public static Typeface get(Context c, String name) {
|
||||
synchronized (cache) {
|
||||
if (!cache.containsKey(name)) {
|
||||
try {
|
||||
Typeface t = Typeface.createFromAsset(c.getAssets(), String.format("fonts/%s", name));
|
||||
cache.put(name, t);
|
||||
return t;
|
||||
} catch (RuntimeException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return cache.get(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.wuadam.awesomewebview.helpers;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Created by Leonardo on 11/23/15.
|
||||
*/
|
||||
public class UrlParser {
|
||||
|
||||
private UrlParser() {
|
||||
}
|
||||
|
||||
public static String getHost(String url) {
|
||||
try {
|
||||
return new URL(url).getHost();
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.wuadam.awesomewebview.jsInterface;
|
||||
|
||||
import android.webkit.JavascriptInterface;
|
||||
|
||||
/**
|
||||
* @Description: Base class for JS interface
|
||||
* @Author: zongheng.wu
|
||||
* @Date: 2/23/21 4:10 PM
|
||||
*/
|
||||
public abstract class BaseJsInterface {
|
||||
|
||||
public BaseJsInterface() {
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String getSimpleName() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.wuadam.awesomewebview.jsInterface;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description: JS interface handler
|
||||
* @Author: zongheng.wu
|
||||
* @Date: 2/23/21 3:36 PM
|
||||
*/
|
||||
public class CommonJsHelper<T extends BaseJsInterface> {
|
||||
private static CommonJsHelper commonJsHelper;
|
||||
|
||||
public static CommonJsHelper getInstance() {
|
||||
if (commonJsHelper == null) {
|
||||
synchronized (CommonJsHelper.class) {
|
||||
if (commonJsHelper == null) {
|
||||
commonJsHelper = new CommonJsHelper();
|
||||
}
|
||||
}
|
||||
}
|
||||
return commonJsHelper;
|
||||
}
|
||||
|
||||
private CommonJsHelper() {
|
||||
}
|
||||
|
||||
private final List<Pair<Class<T>, String>> interfacesInternal = new ArrayList<>(0);
|
||||
|
||||
/**
|
||||
* add new JS interface
|
||||
* @param ifc
|
||||
* @param bridge
|
||||
*/
|
||||
public void addJavascriptInterface(Class<T> ifc, String bridge) {
|
||||
interfacesInternal.add(Pair.create(ifc, bridge));
|
||||
}
|
||||
|
||||
/**
|
||||
* do not call, only for internal
|
||||
* @param webView
|
||||
*/
|
||||
public void addJavascriptInterface(WebView webView) {
|
||||
for (Pair<Class<T>, String> pair: interfacesInternal) {
|
||||
if (pair == null || pair.first == null || TextUtils.isEmpty(pair.second)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
webView.addJavascriptInterface(pair.first.newInstance(), pair.second);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package com.wuadam.awesomewebview.jsInterface;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import com.wuadam.awesomewebview.views.VideoEnabledWebChromeClient;
|
||||
|
||||
public class VideoJsHelper {
|
||||
private VideoEnabledWebChromeClient videoEnabledWebChromeClient;
|
||||
private boolean addedJavascriptInterface;
|
||||
|
||||
public class JavascriptInterface
|
||||
{
|
||||
@android.webkit.JavascriptInterface @SuppressWarnings("unused")
|
||||
public void notifyVideoEnd() // Must match Javascript interface method of VideoEnabledWebChromeClient
|
||||
{
|
||||
Log.d("___", "GOT IT");
|
||||
if (videoEnabledWebChromeClient != null) {
|
||||
// This code is not executed in the UI thread, so we must force that to happen
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
videoEnabledWebChromeClient.onHideCustomView();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the video is being displayed using a custom view (typically full-screen)
|
||||
* @return true it the video is being displayed using a custom view (typically full-screen)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public boolean isVideoFullscreen()
|
||||
{
|
||||
return videoEnabledWebChromeClient != null && videoEnabledWebChromeClient.isVideoFullscreen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass only a VideoEnabledWebChromeClient instance.
|
||||
*/
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
public void setWebChromeClient(WebChromeClient client)
|
||||
{
|
||||
if (client instanceof VideoEnabledWebChromeClient)
|
||||
{
|
||||
this.videoEnabledWebChromeClient = (VideoEnabledWebChromeClient) client;
|
||||
}
|
||||
}
|
||||
|
||||
public void addJavascriptInterface(WebView webView)
|
||||
{
|
||||
if (!addedJavascriptInterface)
|
||||
{
|
||||
// Add javascript interface to be called when the video ends (must be done before page load)
|
||||
//noinspection all
|
||||
webView.addJavascriptInterface(new JavascriptInterface(), "_VideoEnabledWebView"); // Must match Javascript interface name of VideoEnabledWebChromeClient
|
||||
|
||||
addedJavascriptInterface = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,224 @@
|
||||
package com.wuadam.awesomewebview.listeners;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by TheFinestArtist on 1/26/16.
|
||||
*/
|
||||
public class BroadCastManager {
|
||||
|
||||
static final String WEBVIEW_EVENT = "WEBVIEW_EVENT";
|
||||
static final String EXTRA_KEY = "EXTRA_KEY";
|
||||
static final String EXTRA_TYPE = "EXTRA_TYPE";
|
||||
static final String EXTRA_URL = "EXTRA_URL";
|
||||
static final String EXTRA_TITLE = "EXTRA_TITLE";
|
||||
static final String EXTRA_PROGESS = "EXTRA_PROGESS";
|
||||
static final String EXTRA_PRECOMPOSED = "EXTRA_PRECOMPOSED";
|
||||
static final String EXTRA_USER_AGENT = "EXTRA_USER_AGENT";
|
||||
static final String EXTRA_CONTENT_DISPOSITION = "EXTRA_CONTENT_DISPOSITION";
|
||||
static final String EXTRA_MIME_TYPE = "EXTRA_MIME_TYPE";
|
||||
static final String EXTRA_CONTENT_LENGTH = "EXTRA_CONTENT_LENGTH";
|
||||
static final String EXTRA_MENU_CODE = "EXTRA_MENU_CODE";
|
||||
static final String EXTRA_IMAGE_URL = "EXTRA_IMAGE_URL";
|
||||
|
||||
protected int key;
|
||||
protected List<WebViewListener> listeners;
|
||||
protected LocalBroadcastManager manager;
|
||||
protected BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (context == null || intent == null) return;
|
||||
int key = intent.getIntExtra(EXTRA_KEY, Integer.MIN_VALUE);
|
||||
if (BroadCastManager.this.key == key) handleIntent(intent);
|
||||
}
|
||||
};
|
||||
|
||||
public BroadCastManager(Context context, int key, @NonNull List<WebViewListener> listeners) {
|
||||
this.key = key;
|
||||
this.listeners = listeners;
|
||||
manager = LocalBroadcastManager.getInstance(context);
|
||||
manager.registerReceiver(receiver, new IntentFilter(WEBVIEW_EVENT));
|
||||
}
|
||||
|
||||
// Base Static Methods
|
||||
private static Intent getBaseIntent(int key, Type type) {
|
||||
return new Intent(BroadCastManager.WEBVIEW_EVENT).putExtra(EXTRA_KEY, key)
|
||||
.putExtra(EXTRA_TYPE, type);
|
||||
}
|
||||
|
||||
private static void sendBroadCast(Context context, Intent intent) {
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
// Handle Each Event Type
|
||||
public static void onProgressChanged(Context context, int key, int progress) {
|
||||
Intent intent = getBaseIntent(key, Type.PROGRESS_CHANGED).putExtra(EXTRA_PROGESS, progress);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
public static void onReceivedTitle(Context context, int key, String title) {
|
||||
Intent intent = getBaseIntent(key, Type.RECEIVED_TITLE).putExtra(EXTRA_TITLE, title);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
public static void onReceivedTouchIconUrl(Context context, int key, String url,
|
||||
boolean precomposed) {
|
||||
Intent intent = getBaseIntent(key, Type.RECEIVED_TOUCH_ICON_URL).putExtra(EXTRA_URL, url)
|
||||
.putExtra(EXTRA_PRECOMPOSED, precomposed);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
public static void onPageStarted(Context context, int key, String url) {
|
||||
Intent intent = getBaseIntent(key, Type.PAGE_STARTED).putExtra(EXTRA_URL, url);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
public static void onPageFinished(Context context, int key, String url) {
|
||||
Intent intent = getBaseIntent(key, Type.PAGE_FINISHED).putExtra(EXTRA_URL, url);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
public static void onLoadResource(Context context, int key, String url) {
|
||||
Intent intent = getBaseIntent(key, Type.LOAD_RESOURCE).putExtra(EXTRA_URL, url);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
public static void onPageCommitVisible(Context context, int key, String url) {
|
||||
Intent intent = getBaseIntent(key, Type.PAGE_COMMIT_VISIBLE).putExtra(EXTRA_URL, url);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
public static void onDownloadStart(Context context, int key, String url, String userAgent,
|
||||
String contentDisposition, String mimeType, long contentLength) {
|
||||
Intent intent = getBaseIntent(key, Type.DOWNLOADED_START).putExtra(EXTRA_URL, url)
|
||||
.putExtra(EXTRA_USER_AGENT, userAgent)
|
||||
.putExtra(EXTRA_CONTENT_DISPOSITION, contentDisposition)
|
||||
.putExtra(EXTRA_MIME_TYPE, mimeType)
|
||||
.putExtra(EXTRA_CONTENT_LENGTH, contentLength);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
public static void onCustomMenuClick(Context context, int key, String menuCode) {
|
||||
Intent intent = getBaseIntent(key, Type.CUSTOM_MENU_CLICK).putExtra(EXTRA_MENU_CODE, menuCode);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
public static void onClickImage(Context context, int key, String imageUrl) {
|
||||
Intent intent = getBaseIntent(key, Type.CLICK_IMAGE).putExtra(EXTRA_IMAGE_URL, imageUrl);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
public static void unregister(Context context, int key) {
|
||||
Intent intent = getBaseIntent(key, Type.UNREGISTER);
|
||||
sendBroadCast(context, intent);
|
||||
}
|
||||
|
||||
private void handleIntent(@NonNull Intent intent) {
|
||||
Type type = (Type) intent.getSerializableExtra(EXTRA_TYPE);
|
||||
switch (type) {
|
||||
case PROGRESS_CHANGED:
|
||||
onProgressChanged(intent);
|
||||
break;
|
||||
case RECEIVED_TITLE:
|
||||
onReceivedTitle(intent);
|
||||
break;
|
||||
case RECEIVED_TOUCH_ICON_URL:
|
||||
onReceivedTouchIconUrl(intent);
|
||||
break;
|
||||
case PAGE_STARTED:
|
||||
onPageStarted(intent);
|
||||
break;
|
||||
case PAGE_FINISHED:
|
||||
onPageFinished(intent);
|
||||
break;
|
||||
case LOAD_RESOURCE:
|
||||
onLoadResource(intent);
|
||||
break;
|
||||
case PAGE_COMMIT_VISIBLE:
|
||||
onPageCommitVisible(intent);
|
||||
break;
|
||||
case DOWNLOADED_START:
|
||||
onDownloadStart(intent);
|
||||
break;
|
||||
case CUSTOM_MENU_CLICK:
|
||||
onCustomMenuClick(intent);
|
||||
break;
|
||||
case CLICK_IMAGE:
|
||||
onClickImage(intent);
|
||||
break;
|
||||
case UNREGISTER:
|
||||
unregister();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void onProgressChanged(Intent intent) {
|
||||
for (WebViewListener listener : listeners)
|
||||
listener.onProgressChanged(intent.getIntExtra(EXTRA_PROGESS, 0));
|
||||
}
|
||||
|
||||
public void onReceivedTitle(Intent intent) {
|
||||
for (WebViewListener listener : listeners)
|
||||
listener.onReceivedTitle(intent.getStringExtra(EXTRA_TITLE));
|
||||
}
|
||||
|
||||
public void onReceivedTouchIconUrl(Intent intent) {
|
||||
for (WebViewListener listener : listeners)
|
||||
listener.onReceivedTouchIconUrl(intent.getStringExtra(EXTRA_URL),
|
||||
intent.getBooleanExtra(EXTRA_PRECOMPOSED, false));
|
||||
}
|
||||
|
||||
public void onPageStarted(Intent intent) {
|
||||
for (WebViewListener listener : listeners)
|
||||
listener.onPageStarted(intent.getStringExtra(EXTRA_URL));
|
||||
}
|
||||
|
||||
public void onPageFinished(Intent intent) {
|
||||
for (WebViewListener listener : listeners)
|
||||
listener.onPageFinished(intent.getStringExtra(EXTRA_URL));
|
||||
}
|
||||
|
||||
public void onLoadResource(Intent intent) {
|
||||
for (WebViewListener listener : listeners)
|
||||
listener.onLoadResource(intent.getStringExtra(EXTRA_URL));
|
||||
}
|
||||
|
||||
public void onPageCommitVisible(Intent intent) {
|
||||
for (WebViewListener listener : listeners)
|
||||
listener.onPageCommitVisible(intent.getStringExtra(EXTRA_URL));
|
||||
}
|
||||
|
||||
public void onDownloadStart(Intent intent) {
|
||||
for (WebViewListener listener : listeners)
|
||||
listener.onDownloadStart(intent.getStringExtra(EXTRA_URL),
|
||||
intent.getStringExtra(EXTRA_USER_AGENT), intent.getStringExtra(EXTRA_CONTENT_DISPOSITION),
|
||||
intent.getStringExtra(EXTRA_MIME_TYPE), intent.getLongExtra(EXTRA_CONTENT_LENGTH, 0l));
|
||||
}
|
||||
|
||||
public void onCustomMenuClick(Intent intent) {
|
||||
for (WebViewListener listener: listeners) {
|
||||
listener.onCustomMenuClick(intent.getStringExtra(EXTRA_MENU_CODE));
|
||||
}
|
||||
}
|
||||
|
||||
public void onClickImage(Intent intent) {
|
||||
for (WebViewListener listener: listeners) {
|
||||
listener.onClickImage(intent.getStringExtra(EXTRA_IMAGE_URL));
|
||||
}
|
||||
}
|
||||
|
||||
private void unregister() {
|
||||
if (manager != null && receiver != null) manager.unregisterReceiver(receiver);
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
PROGRESS_CHANGED, RECEIVED_TITLE, RECEIVED_TOUCH_ICON_URL, PAGE_STARTED, PAGE_FINISHED, LOAD_RESOURCE, PAGE_COMMIT_VISIBLE, DOWNLOADED_START, CUSTOM_MENU_CLICK, CLICK_IMAGE, UNREGISTER
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.wuadam.awesomewebview.listeners;
|
||||
|
||||
/**
|
||||
* Created by TheFinestArtist on 1/26/16.
|
||||
*/
|
||||
public abstract class WebViewListener {
|
||||
|
||||
public void onProgressChanged(int progress) {
|
||||
}
|
||||
|
||||
public void onReceivedTitle(String title) {
|
||||
}
|
||||
|
||||
public void onReceivedTouchIconUrl(String url, boolean precomposed) {
|
||||
}
|
||||
|
||||
public void onPageStarted(String url) {
|
||||
}
|
||||
|
||||
public void onPageFinished(String url) {
|
||||
}
|
||||
|
||||
public void onLoadResource(String url) {
|
||||
}
|
||||
|
||||
public void onPageCommitVisible(String url) {
|
||||
}
|
||||
|
||||
public void onDownloadStart(String url, String userAgent, String contentDisposition,
|
||||
String mimeType, long contentLength) {
|
||||
}
|
||||
|
||||
public void onCustomMenuClick(String menuCode) {
|
||||
}
|
||||
|
||||
public void onClickImage(String imageUrl) {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.wuadam.awesomewebview.objects;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class CustomMenu implements Serializable {
|
||||
private int titleRes;
|
||||
private String code;
|
||||
|
||||
public CustomMenu(@StringRes int titleRes, String code) {
|
||||
this.titleRes = titleRes;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getTitleRes() {
|
||||
return titleRes;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,187 @@
|
||||
package com.wuadam.awesomewebview.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.Build;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.wuadam.awesomewebview.R;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Leonardo on 11/26/15.
|
||||
*/
|
||||
|
||||
public class ShadowLayout extends FrameLayout {
|
||||
|
||||
private int shadowColor;
|
||||
private float shadowSize;
|
||||
private float cornerRadius;
|
||||
private float dx;
|
||||
private float dy;
|
||||
|
||||
public ShadowLayout(Context context) {
|
||||
super(context);
|
||||
setWillNotDraw(false);
|
||||
initAttributes(null);
|
||||
setPadding();
|
||||
}
|
||||
|
||||
public ShadowLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
initAttributes(attrs);
|
||||
setPadding();
|
||||
}
|
||||
|
||||
public ShadowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setWillNotDraw(false);
|
||||
initAttributes(attrs);
|
||||
setPadding();
|
||||
}
|
||||
|
||||
private void initAttributes(AttributeSet attrs) {
|
||||
TypedArray attr = getContext().obtainStyledAttributes(attrs, R.styleable.ShadowLayout, 0, 0);
|
||||
if (attr == null) return;
|
||||
|
||||
try {
|
||||
cornerRadius = attr.getDimension(R.styleable.ShadowLayout_slCornerRadius,
|
||||
getResources().getDimension(R.dimen.defaultMenuDropShadowCornerRadius));
|
||||
shadowSize = attr.getDimension(R.styleable.ShadowLayout_slShadowSize,
|
||||
getResources().getDimension(R.dimen.defaultMenuDropShadowSize));
|
||||
dx = attr.getDimension(R.styleable.ShadowLayout_slDx, 0);
|
||||
dy = attr.getDimension(R.styleable.ShadowLayout_slDy, 0);
|
||||
shadowColor = attr.getColor(R.styleable.ShadowLayout_slShadowColor,
|
||||
ContextCompat.getColor(getContext(), R.color.finestBlack10));
|
||||
} finally {
|
||||
attr.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
private void setPadding() {
|
||||
int xPadding = (int) (shadowSize + Math.abs(dx));
|
||||
int yPadding = (int) (shadowSize + Math.abs(dy));
|
||||
setPadding(xPadding, yPadding, xPadding, yPadding);
|
||||
}
|
||||
|
||||
public void setShadowColor(int shadowColor) {
|
||||
this.shadowColor = shadowColor;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setShadowSize(float shadowSize) {
|
||||
this.shadowSize = shadowSize;
|
||||
setPadding();
|
||||
}
|
||||
|
||||
public void setCornerRadius(float cornerRadius) {
|
||||
this.cornerRadius = cornerRadius;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setDx(float dx) {
|
||||
this.dx = dx;
|
||||
setPadding();
|
||||
}
|
||||
|
||||
public void setDy(float dy) {
|
||||
this.dy = dy;
|
||||
setPadding();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
// RoundRectShape rss = new RoundRectShape(new float[]{12f, 12f, 12f,
|
||||
// 12f, 12f, 12f, 12f, 12f}, null, null);
|
||||
// ShapeDrawable sds = new ShapeDrawable(rss);
|
||||
// sds.setShaderFactory(new ShapeDrawable.ShaderFactory() {
|
||||
//
|
||||
// @Override
|
||||
// public Shader resize(int width, int height) {
|
||||
// LinearGradient lg = new LinearGradient(0, 0, 0, height,
|
||||
// new int[]{Color.parseColor("#e5e5e5"),
|
||||
// Color.parseColor("#e5e5e5"),
|
||||
// Color.parseColor("#e5e5e5"),
|
||||
// Color.parseColor("#e5e5e5")}, new float[]{0,
|
||||
// 0.50f, 0.50f, 1}, Shader.TileMode.REPEAT);
|
||||
// return lg;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// LayerDrawable ld = new LayerDrawable(new Drawable[]{sds, sds});
|
||||
// ld.setLayerInset(0, 5, 5, 0, 0); // inset the shadow so it doesn't start right at the left/top
|
||||
// ld.setLayerInset(1, 0, 0, 5, 5); // inset the top drawable so we can leave a bit of space for the shadow to use
|
||||
|
||||
setBackgroundCompat(canvas.getWidth(), canvas.getHeight());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void setBackgroundCompat(int w, int h) {
|
||||
Bitmap bitmap =
|
||||
createShadowBitmap(w, h, cornerRadius, shadowSize, dx, dy, shadowColor, Color.TRANSPARENT);
|
||||
// Bitmap coloredBitmap = BitmapHelper.getColoredBitmap(getContext(), bitmap, shadowColor);
|
||||
BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap);
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
setBackgroundDrawable(drawable);
|
||||
} else {
|
||||
setBackground(drawable);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap createShadowBitmap(int shadowWidth, int shadowHeight, float cornerRadius,
|
||||
float shadowSize, float dx, float dy, int shadowColor, int fillColor) {
|
||||
|
||||
Bitmap output = Bitmap.createBitmap(shadowWidth, shadowHeight, Bitmap.Config.ALPHA_8);
|
||||
Canvas canvas = new Canvas(output);
|
||||
|
||||
RectF shadowRect =
|
||||
new RectF(shadowSize, shadowSize, shadowWidth - shadowSize, shadowHeight - shadowSize);
|
||||
|
||||
if (dy > 0) {
|
||||
shadowRect.top += dy;
|
||||
shadowRect.bottom -= dy;
|
||||
} else if (dy < 0) {
|
||||
shadowRect.top += Math.abs(dy);
|
||||
shadowRect.bottom -= Math.abs(dy);
|
||||
}
|
||||
|
||||
if (dx > 0) {
|
||||
shadowRect.left += dx;
|
||||
shadowRect.right -= dx;
|
||||
} else if (dx < 0) {
|
||||
shadowRect.left += Math.abs(dx);
|
||||
shadowRect.right -= Math.abs(dx);
|
||||
}
|
||||
|
||||
Paint shadowPaint = new Paint();
|
||||
shadowPaint.setAntiAlias(true);
|
||||
shadowPaint.setColor(fillColor);
|
||||
shadowPaint.setStyle(Paint.Style.FILL);
|
||||
shadowPaint.setShadowLayer(shadowSize, dx, dy, shadowColor);
|
||||
|
||||
canvas.drawRoundRect(shadowRect, cornerRadius, cornerRadius, shadowPaint);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSuggestedMinimumWidth() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSuggestedMinimumHeight() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,283 @@
|
||||
package com.wuadam.awesomewebview.views;
|
||||
|
||||
import android.media.MediaPlayer;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* This class serves as a WebChromeClient to be set to a WebView, allowing it to play video.
|
||||
* Video will play differently depending on target API level (in-line, fullscreen, or both).
|
||||
*
|
||||
* It has been tested with the following video classes:
|
||||
* - android.widget.VideoView (typically API level <11)
|
||||
* - android.webkit.HTML5VideoFullScreen$VideoSurfaceView/VideoTextureView (typically API level 11-18)
|
||||
* - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView (typically API level 19+)
|
||||
*
|
||||
* Important notes:
|
||||
* - For API level 11+, android:hardwareAccelerated="true" must be set in the application manifest.
|
||||
* - The invoking activity must call VideoEnabledWebChromeClient's onBackPressed() inside of its own onBackPressed().
|
||||
* - Tested in Android API levels 8-19. Only tested on http://m.youtube.com.
|
||||
*
|
||||
* @author Cristian Perez (http://cpr.name)
|
||||
*
|
||||
*/
|
||||
public class VideoEnabledWebChromeClient extends WebChromeClient implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener
|
||||
{
|
||||
public interface ToggledFullscreenCallback
|
||||
{
|
||||
void toggledFullscreen(boolean fullscreen);
|
||||
}
|
||||
|
||||
private View activityNonVideoView;
|
||||
private ViewGroup activityVideoView;
|
||||
private View loadingView;
|
||||
private WebView webView;
|
||||
|
||||
private boolean isVideoFullscreen; // Indicates if the video is being displayed using a custom view (typically full-screen)
|
||||
private FrameLayout videoViewContainer;
|
||||
private CustomViewCallback videoViewCallback;
|
||||
|
||||
private ToggledFullscreenCallback toggledFullscreenCallback;
|
||||
|
||||
/**
|
||||
* Never use this constructor alone.
|
||||
* This constructor allows this class to be defined as an inline inner class in which the user can override methods
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public VideoEnabledWebChromeClient()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a video enabled WebChromeClient.
|
||||
* @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
|
||||
* @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView)
|
||||
{
|
||||
this.activityNonVideoView = activityNonVideoView;
|
||||
this.activityVideoView = activityVideoView;
|
||||
this.loadingView = null;
|
||||
this.webView = null;
|
||||
this.isVideoFullscreen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a video enabled WebChromeClient.
|
||||
* @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
|
||||
* @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
|
||||
* @param loadingView A View to be shown while the video is loading (typically only used in API level <11). Must be already inflated and not attached to a parent view.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView)
|
||||
{
|
||||
this.activityNonVideoView = activityNonVideoView;
|
||||
this.activityVideoView = activityVideoView;
|
||||
this.loadingView = loadingView;
|
||||
this.webView = null;
|
||||
this.isVideoFullscreen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a video enabled WebChromeClient.
|
||||
* @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
|
||||
* @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
|
||||
* @param loadingView A View to be shown while the video is loading (typically only used in API level <11). Must be already inflated and not attached to a parent view.
|
||||
* @param webView The owner VideoEnabledWebView. Passing it will enable the VideoEnabledWebChromeClient to detect the HTML5 video ended event and exit full-screen.
|
||||
* Note: The web page must only contain one video tag in order for the HTML5 video ended event to work. This could be improved if needed (see Javascript code).
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView, WebView webView)
|
||||
{
|
||||
this.activityNonVideoView = activityNonVideoView;
|
||||
this.activityVideoView = activityVideoView;
|
||||
this.loadingView = loadingView;
|
||||
this.webView = webView;
|
||||
this.isVideoFullscreen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the video is being displayed using a custom view (typically full-screen)
|
||||
* @return true it the video is being displayed using a custom view (typically full-screen)
|
||||
*/
|
||||
public boolean isVideoFullscreen()
|
||||
{
|
||||
return isVideoFullscreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that will be fired when the video starts or finishes displaying using a custom view (typically full-screen)
|
||||
* @param callback A VideoEnabledWebChromeClient.ToggledFullscreenCallback callback
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setOnToggledFullscreen(ToggledFullscreenCallback callback)
|
||||
{
|
||||
this.toggledFullscreenCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowCustomView(View view, CustomViewCallback callback)
|
||||
{
|
||||
if (view instanceof FrameLayout)
|
||||
{
|
||||
// A video wants to be shown
|
||||
FrameLayout frameLayout = (FrameLayout) view;
|
||||
View focusedChild = frameLayout.getFocusedChild();
|
||||
|
||||
// Save video related variables
|
||||
this.isVideoFullscreen = true;
|
||||
this.videoViewContainer = frameLayout;
|
||||
this.videoViewCallback = callback;
|
||||
|
||||
// Hide the non-video view, add the video view, and show it
|
||||
activityNonVideoView.setVisibility(View.INVISIBLE);
|
||||
activityVideoView.setVisibility(View.VISIBLE);
|
||||
activityVideoView.addView(videoViewContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
activityVideoView.setVisibility(View.VISIBLE);
|
||||
|
||||
if (focusedChild instanceof android.widget.VideoView)
|
||||
{
|
||||
// android.widget.VideoView (typically API level <11)
|
||||
android.widget.VideoView videoView = (android.widget.VideoView) focusedChild;
|
||||
|
||||
// Handle all the required events
|
||||
videoView.setOnPreparedListener(this);
|
||||
videoView.setOnCompletionListener(this);
|
||||
videoView.setOnErrorListener(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other classes, including:
|
||||
// - android.webkit.HTML5VideoFullScreen$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 11-18)
|
||||
// - android.webkit.HTML5VideoFullScreen$VideoTextureView, which inherits from android.view.TextureView (typically API level 11-18)
|
||||
// - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 19+)
|
||||
|
||||
// Handle HTML5 video ended event only if the class is a SurfaceView
|
||||
// Test case: TextureView of Sony Xperia T API level 16 doesn't work fullscreen when loading the javascript below
|
||||
if (webView != null && webView.getSettings().getJavaScriptEnabled() && focusedChild instanceof SurfaceView)
|
||||
{
|
||||
// Run javascript code that detects the video end and notifies the Javascript interface
|
||||
String js = "javascript:";
|
||||
js += "var _ytrp_html5_video_last;";
|
||||
js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];";
|
||||
js += "if (_ytrp_html5_video != undefined && _ytrp_html5_video != _ytrp_html5_video_last) {";
|
||||
{
|
||||
js += "_ytrp_html5_video_last = _ytrp_html5_video;";
|
||||
js += "function _ytrp_html5_video_ended() {";
|
||||
{
|
||||
js += "_VideoEnabledWebView.notifyVideoEnd();"; // Must match Javascript interface name and method of VideoEnableWebView
|
||||
}
|
||||
js += "}";
|
||||
js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);";
|
||||
}
|
||||
js += "}";
|
||||
webView.loadUrl(js);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify full-screen change
|
||||
if (toggledFullscreenCallback != null)
|
||||
{
|
||||
toggledFullscreenCallback.toggledFullscreen(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override @SuppressWarnings("deprecation")
|
||||
public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) // Available in API level 14+, deprecated in API level 18+
|
||||
{
|
||||
onShowCustomView(view, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHideCustomView()
|
||||
{
|
||||
// This method should be manually called on video end in all cases because it's not always called automatically.
|
||||
// This method must be manually called on back key press (from this class' onBackPressed() method).
|
||||
|
||||
if (isVideoFullscreen)
|
||||
{
|
||||
// Hide the video view, remove it, and show the non-video view
|
||||
activityVideoView.setVisibility(View.INVISIBLE);
|
||||
activityVideoView.removeView(videoViewContainer);
|
||||
activityNonVideoView.setVisibility(View.VISIBLE);
|
||||
|
||||
// Call back (only in API level <19, because in API level 19+ with chromium webview it crashes)
|
||||
if (videoViewCallback != null && !videoViewCallback.getClass().getName().contains(".chromium."))
|
||||
{
|
||||
videoViewCallback.onCustomViewHidden();
|
||||
}
|
||||
|
||||
// Reset video related variables
|
||||
isVideoFullscreen = false;
|
||||
videoViewContainer = null;
|
||||
videoViewCallback = null;
|
||||
|
||||
// Notify full-screen change
|
||||
if (toggledFullscreenCallback != null)
|
||||
{
|
||||
toggledFullscreenCallback.toggledFullscreen(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getVideoLoadingProgressView() // Video will start loading
|
||||
{
|
||||
if (loadingView != null)
|
||||
{
|
||||
loadingView.setVisibility(View.VISIBLE);
|
||||
return loadingView;
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.getVideoLoadingProgressView();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepared(MediaPlayer mp) // Video will start playing, only called in the case of android.widget.VideoView (typically API level <11)
|
||||
{
|
||||
if (loadingView != null)
|
||||
{
|
||||
loadingView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) // Video finished playing, only called in the case of android.widget.VideoView (typically API level <11)
|
||||
{
|
||||
onHideCustomView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onError(MediaPlayer mp, int what, int extra) // Error while playing video, only called in the case of android.widget.VideoView (typically API level <11)
|
||||
{
|
||||
return false; // By returning false, onCompletion() will be called
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the class that the back key has been pressed by the user.
|
||||
* This must be called from the Activity's onBackPressed(), and if it returns false, the activity itself should handle it. Otherwise don't do anything.
|
||||
* @return Returns true if the event was handled, and false if was not (video view is not visible)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public boolean onBackPressed()
|
||||
{
|
||||
if (isVideoFullscreen)
|
||||
{
|
||||
onHideCustomView();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
package com.wuadam.awesomewebview.views;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import com.wuadam.awesomewebview.jsInterface.VideoJsHelper;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class serves as a WebView to be used in conjunction with a VideoEnabledWebChromeClient.
|
||||
* It makes possible:
|
||||
* - To detect the HTML5 video ended event so that the VideoEnabledWebChromeClient can exit full-screen.
|
||||
*
|
||||
* Important notes:
|
||||
* - Javascript is enabled by default and must not be disabled with getSettings().setJavaScriptEnabled(false).
|
||||
* - setWebChromeClient() must be called before any loadData(), loadDataWithBaseURL() or loadUrl() method.
|
||||
*
|
||||
* @author Cristian Perez (http://cpr.name)
|
||||
*
|
||||
*/
|
||||
public class VideoEnabledWebView extends WebView
|
||||
{
|
||||
private VideoJsHelper videoJsHelper;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public VideoEnabledWebView(Context context)
|
||||
{
|
||||
super(context);
|
||||
videoJsHelper = new VideoJsHelper();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public VideoEnabledWebView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
videoJsHelper = new VideoJsHelper();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public VideoEnabledWebView(Context context, AttributeSet attrs, int defStyle)
|
||||
{
|
||||
super(context, attrs, defStyle);
|
||||
videoJsHelper = new VideoJsHelper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the video is being displayed using a custom view (typically full-screen)
|
||||
* @return true it the video is being displayed using a custom view (typically full-screen)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public boolean isVideoFullscreen()
|
||||
{
|
||||
return videoJsHelper.isVideoFullscreen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass a VideoEnabledWebChromeClient instance to enable video full-screen.
|
||||
*/
|
||||
@Override @SuppressLint("SetJavaScriptEnabled")
|
||||
public void setWebChromeClient(WebChromeClient client)
|
||||
{
|
||||
getSettings().setJavaScriptEnabled(true);
|
||||
|
||||
videoJsHelper.setWebChromeClient(client);
|
||||
|
||||
super.setWebChromeClient(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadData(String data, String mimeType, String encoding)
|
||||
{
|
||||
videoJsHelper.addJavascriptInterface(this);
|
||||
super.loadData(data, mimeType, encoding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
|
||||
{
|
||||
videoJsHelper.addJavascriptInterface(this);
|
||||
super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadUrl(String url)
|
||||
{
|
||||
videoJsHelper.addJavascriptInterface(this);
|
||||
super.loadUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadUrl(String url, Map<String, String> additionalHttpHeaders)
|
||||
{
|
||||
videoJsHelper.addJavascriptInterface(this);
|
||||
super.loadUrl(url, additionalHttpHeaders);
|
||||
}
|
||||
|
||||
}
|
||||
3
library/src/main/res/anim/accelerate_cubic.xml
Executable file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:factor="1.5"/>
|
||||
3
library/src/main/res/anim/accelerate_quart.xml
Executable file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:factor="2.0"/>
|
||||
4
library/src/main/res/anim/accelerate_quint.xml
Executable file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<accelerateInterpolator
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:factor="2.5"/>
|
||||
23
library/src/main/res/anim/activity_close_enter.xml
Executable file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@android:color/black"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="true"
|
||||
android:fillEnabled="true"
|
||||
android:shareInterpolator="false"
|
||||
android:zAdjustment="top">
|
||||
<alpha
|
||||
android:duration="250"
|
||||
android:fromAlpha="0.2"
|
||||
android:interpolator="@anim/accelerate_cubic"
|
||||
android:toAlpha="1.0"/>
|
||||
<scale
|
||||
android:duration="250"
|
||||
android:fromXScale="0.9"
|
||||
android:fromYScale="0.9"
|
||||
android:interpolator="@anim/accelerate_cubic"
|
||||
android:pivotX="50.0%p"
|
||||
android:pivotY="50.0%p"
|
||||
android:toXScale="1.0"
|
||||
android:toYScale="1.0"/>
|
||||
</set>
|
||||
9
library/src/main/res/anim/activity_close_exit.xml
Executable file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:zAdjustment="top">
|
||||
<translate
|
||||
android:duration="250"
|
||||
android:fromXDelta="0.0%p"
|
||||
android:interpolator="@anim/accelerate_cubic"
|
||||
android:toXDelta="100.0%p"/>
|
||||
</set>
|
||||
9
library/src/main/res/anim/activity_open_enter.xml
Executable file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:zAdjustment="top">
|
||||
<translate
|
||||
android:duration="250"
|
||||
android:fromXDelta="100.0%p"
|
||||
android:interpolator="@anim/decelerate_cubic"
|
||||
android:toXDelta="0.0%p"/>
|
||||
</set>
|
||||
23
library/src/main/res/anim/activity_open_exit.xml
Executable file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@android:color/black"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="false"
|
||||
android:fillEnabled="true"
|
||||
android:shareInterpolator="false"
|
||||
android:zAdjustment="normal">
|
||||
<alpha
|
||||
android:duration="250"
|
||||
android:fromAlpha="1.0"
|
||||
android:interpolator="@anim/decelerate_cubic"
|
||||
android:toAlpha="0.2"/>
|
||||
<scale
|
||||
android:duration="250"
|
||||
android:fromXScale="1.0"
|
||||
android:fromYScale="1.0"
|
||||
android:interpolator="@anim/decelerate_cubic"
|
||||
android:pivotX="50.0%p"
|
||||
android:pivotY="50.0%p"
|
||||
android:toXScale="0.9"
|
||||
android:toYScale="0.9"/>
|
||||
</set>
|
||||
3
library/src/main/res/anim/decelerate_cubic.xml
Executable file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:factor="1.5"/>
|
||||
4
library/src/main/res/anim/decelerate_quart.xml
Executable file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<decelerateInterpolator
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:factor="2.0"/>
|
||||
4
library/src/main/res/anim/decelerate_quint.xml
Executable file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<decelerateInterpolator
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:factor="2.5"/>
|
||||
8
library/src/main/res/anim/fade_in_fast.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:duration="250"
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"/>
|
||||
</set>
|
||||
8
library/src/main/res/anim/fade_in_medium.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:duration="400"
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"/>
|
||||
</set>
|
||||
8
library/src/main/res/anim/fade_out_fast.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:duration="250"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"/>
|
||||
</set>
|
||||
9
library/src/main/res/anim/fade_out_medium.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:duration="800"
|
||||
android:fromAlpha="1.0"
|
||||
android:interpolator="@android:anim/accelerate_interpolator"
|
||||
android:toAlpha="0.0"/>
|
||||
</set>
|
||||
23
library/src/main/res/anim/fragment_close_enter.xml
Executable file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false"
|
||||
android:zAdjustment="top">
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="false"
|
||||
android:fillEnabled="true"
|
||||
android:fromAlpha="1.0"
|
||||
android:interpolator="@anim/accelerate_quart"
|
||||
android:startOffset="100"
|
||||
android:toAlpha="0.0"/>
|
||||
<translate
|
||||
android:duration="250"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="true"
|
||||
android:fillEnabled="true"
|
||||
android:fromYDelta="0.0%"
|
||||
android:interpolator="@anim/accelerate_quint"
|
||||
android:toYDelta="-4.999995%"/>
|
||||
</set>
|
||||
23
library/src/main/res/anim/fragment_close_exit.xml
Executable file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false"
|
||||
android:zAdjustment="top">
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="false"
|
||||
android:fillEnabled="true"
|
||||
android:fromAlpha="1.0"
|
||||
android:interpolator="@anim/accelerate_quart"
|
||||
android:startOffset="100"
|
||||
android:toAlpha="0.0"/>
|
||||
<translate
|
||||
android:duration="250"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="true"
|
||||
android:fillEnabled="true"
|
||||
android:fromYDelta="0.0%"
|
||||
android:interpolator="@anim/accelerate_quint"
|
||||
android:toYDelta="4.999995%"/>
|
||||
</set>
|
||||
22
library/src/main/res/anim/fragment_open_enter.xml
Executable file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false"
|
||||
android:zAdjustment="top">
|
||||
<alpha
|
||||
android:duration="200"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="false"
|
||||
android:fillEnabled="true"
|
||||
android:fromAlpha="0.0"
|
||||
android:interpolator="@anim/decelerate_quart"
|
||||
android:toAlpha="1.0"/>
|
||||
<translate
|
||||
android:duration="350"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="true"
|
||||
android:fillEnabled="true"
|
||||
android:fromYDelta="8.000004%"
|
||||
android:interpolator="@anim/decelerate_quint"
|
||||
android:toYDelta="0.0"/>
|
||||
</set>
|
||||
22
library/src/main/res/anim/fragment_open_exit.xml
Executable file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false"
|
||||
android:zAdjustment="top">
|
||||
<alpha
|
||||
android:duration="200"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="false"
|
||||
android:fillEnabled="true"
|
||||
android:fromAlpha="0.0"
|
||||
android:interpolator="@anim/decelerate_quart"
|
||||
android:toAlpha="1.0"/>
|
||||
<translate
|
||||
android:duration="350"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="true"
|
||||
android:fillEnabled="true"
|
||||
android:fromYDelta="-8.000004%"
|
||||
android:interpolator="@anim/decelerate_quint"
|
||||
android:toYDelta="0.0"/>
|
||||
</set>
|
||||
8
library/src/main/res/anim/hold.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="800"
|
||||
android:fromYDelta="0.0%p"
|
||||
android:toYDelta="0.0%p"/>
|
||||
</set>
|
||||
23
library/src/main/res/anim/modal_activity_close_enter.xml
Executable file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@android:color/black"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="true"
|
||||
android:fillEnabled="true"
|
||||
android:shareInterpolator="false"
|
||||
android:zAdjustment="top">
|
||||
<alpha
|
||||
android:duration="250"
|
||||
android:fromAlpha="0.2"
|
||||
android:interpolator="@anim/accelerate_cubic"
|
||||
android:toAlpha="1.0"/>
|
||||
<scale
|
||||
android:duration="250"
|
||||
android:fromXScale="0.9"
|
||||
android:fromYScale="0.9"
|
||||
android:interpolator="@anim/accelerate_cubic"
|
||||
android:pivotX="50.0%p"
|
||||
android:pivotY="50.0%p"
|
||||
android:toXScale="1.0"
|
||||
android:toYScale="1.0"/>
|
||||
</set>
|
||||
9
library/src/main/res/anim/modal_activity_close_exit.xml
Executable file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:zAdjustment="top">
|
||||
<translate
|
||||
android:duration="250"
|
||||
android:fromYDelta="0.0%p"
|
||||
android:interpolator="@anim/accelerate_cubic"
|
||||
android:toYDelta="100.0%p"/>
|
||||
</set>
|
||||
9
library/src/main/res/anim/modal_activity_open_enter.xml
Executable file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:zAdjustment="top">
|
||||
<translate
|
||||
android:duration="250"
|
||||
android:fromYDelta="100.0%p"
|
||||
android:interpolator="@anim/decelerate_cubic"
|
||||
android:toYDelta="0.0%p"/>
|
||||
</set>
|
||||
23
library/src/main/res/anim/modal_activity_open_exit.xml
Executable file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@android:color/black"
|
||||
android:fillAfter="true"
|
||||
android:fillBefore="false"
|
||||
android:fillEnabled="true"
|
||||
android:shareInterpolator="false"
|
||||
android:zAdjustment="normal">
|
||||
<alpha
|
||||
android:duration="250"
|
||||
android:fromAlpha="1.0"
|
||||
android:interpolator="@anim/decelerate_cubic"
|
||||
android:toAlpha="0.2"/>
|
||||
<scale
|
||||
android:duration="250"
|
||||
android:fromXScale="1.0"
|
||||
android:fromYScale="1.0"
|
||||
android:interpolator="@anim/decelerate_cubic"
|
||||
android:pivotX="50.0%p"
|
||||
android:pivotY="50.0%p"
|
||||
android:toXScale="0.9"
|
||||
android:toYScale="0.9"/>
|
||||
</set>
|
||||
6
library/src/main/res/anim/none.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="0"
|
||||
android:fromAlpha="1.0"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
android:toAlpha="1.0"/>
|
||||
14
library/src/main/res/anim/popup_flyout_hide.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="125">
|
||||
<scale
|
||||
android:fromXScale="1.0"
|
||||
android:fromYScale="1.0"
|
||||
android:pivotX="100%"
|
||||
android:pivotY="0%"
|
||||
android:toXScale="0.15"
|
||||
android:toYScale="0.0"/>
|
||||
<alpha
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"/>
|
||||
</set>
|
||||
15
library/src/main/res/anim/popup_flyout_show.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="125">
|
||||
<scale
|
||||
android:fromXScale="0.15"
|
||||
android:fromYScale="0.0"
|
||||
android:pivotX="100%"
|
||||
android:pivotY="0%"
|
||||
android:toXScale="1.0"
|
||||
android:toYScale="1.0"/>
|
||||
<alpha
|
||||
android:fromAlpha="0.2"
|
||||
android:startOffset="60"
|
||||
android:toAlpha="1.0"/>
|
||||
</set>
|
||||
8
library/src/main/res/anim/slide_down.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="350"
|
||||
android:fromYDelta="0.0%"
|
||||
android:toYDelta="100.0%"/>
|
||||
</set>
|
||||
8
library/src/main/res/anim/slide_left_in.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="300"
|
||||
android:fromXDelta="100.0%p"
|
||||
android:toXDelta="0.0"/>
|
||||
</set>
|
||||
8
library/src/main/res/anim/slide_right_out.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="300"
|
||||
android:fromXDelta="0.0%"
|
||||
android:toXDelta="100.0%p"/>
|
||||
</set>
|
||||
8
library/src/main/res/anim/slide_up.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="350"
|
||||
android:fromYDelta="100.0%"
|
||||
android:toYDelta="0.0%"/>
|
||||
</set>
|
||||
BIN
library/src/main/res/drawable-hdpi/back.png
Executable file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
library/src/main/res/drawable-hdpi/close.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
library/src/main/res/drawable-hdpi/forward.png
Executable file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
library/src/main/res/drawable-hdpi/more.png
Executable file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
library/src/main/res/drawable-mdpi/back.png
Executable file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
library/src/main/res/drawable-mdpi/close.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
library/src/main/res/drawable-mdpi/forward.png
Executable file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
library/src/main/res/drawable-mdpi/more.png
Executable file
|
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/finestWhite30">
|
||||
<item android:drawable="@drawable/selector_dark_theme_holo"/>
|
||||
</ripple>
|
||||
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="@color/finestWhite10"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_focused="true">
|
||||
<shape>
|
||||
<solid android:color="@color/finestWhite10"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:drawable="@android:color/transparent"/>
|
||||
</selector>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/finestBlack30">
|
||||
<item android:drawable="@drawable/selector_light_theme_holo"/>
|
||||
</ripple>
|
||||
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<solid android:color="@color/finestGray10"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_focused="true">
|
||||
<shape>
|
||||
<solid android:color="@color/finestGray10"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:drawable="@android:color/transparent"/>
|
||||
</selector>
|
||||
BIN
library/src/main/res/drawable-xhdpi/back.png
Executable file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
library/src/main/res/drawable-xhdpi/close.png
Executable file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
library/src/main/res/drawable-xhdpi/forward.png
Executable file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
library/src/main/res/drawable-xhdpi/more.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
library/src/main/res/drawable-xxhdpi/back.png
Executable file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
library/src/main/res/drawable-xxhdpi/close.png
Executable file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
library/src/main/res/drawable-xxhdpi/forward.png
Executable file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
library/src/main/res/drawable-xxhdpi/more.png
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
11
library/src/main/res/drawable/back_vector.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="48dp"
|
||||
android:viewportHeight="144.0"
|
||||
android:viewportWidth="144.0"
|
||||
android:width="48dp">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M84.5,89.3L64.4,70.5l20.2,-18.8c1.2,-1.1 1.3,-3 0.2,-4.2c-1.1,-1.2 -3,-1.3 -4.2,-0.2L58,68.3c-0.6,0.6 -1,1.4 -1,2.2c0,0.8 0.3,1.6 1,2.2l22.5,21c1.2,1.1 3.1,1.1 4.2,-0.1C85.8,92.3 85.8,90.4 84.5,89.3L84.5,89.3z"/>
|
||||
</vector>
|
||||
11
library/src/main/res/drawable/close_vector.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="48dp"
|
||||
android:viewportHeight="144.0"
|
||||
android:viewportWidth="144.0"
|
||||
android:width="48dp">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M76.8,72l16.3,-15.8c1.2,-1.2 1.2,-3.1 0.1,-4.2c-1.2,-1.2 -3.1,-1.2 -4.2,-0.1l-16.4,16l-16.4,-16c-1.2,-1.2 -3.1,-1.1 -4.2,0.1c-1.2,1.2 -1.1,3.1 0.1,4.2L68.2,72L51.9,87.8c-1.2,1.2 -1.2,3.1 -0.1,4.2c1.2,1.2 3.1,1.2 4.2,0.1l16.4,-16l16.4,16c1.2,1.2 3.1,1.1 4.2,-0.1c1.2,-1.2 1.1,-3.1 -0.1,-4.2L76.8,72z"/>
|
||||
</vector>
|
||||
11
library/src/main/res/drawable/forward_vector.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="48dp"
|
||||
android:viewportHeight="144.0"
|
||||
android:viewportWidth="144.0"
|
||||
android:width="48dp">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M65.5,93.7l22.5,-21c0.6,-0.6 1,-1.4 1,-2.2c0,-0.8 -0.3,-1.6 -1,-2.2l-22.6,-21c-1.2,-1.1 -3.1,-1.1 -4.2,0.2c-1.1,1.2 -1.1,3.1 0.2,4.2l20.2,18.8L61.5,89.3c-1.2,1.1 -1.3,3 -0.1,4.2C62.4,94.8 64.3,94.8 65.5,93.7L65.5,93.7z"/>
|
||||
</vector>
|
||||
17
library/src/main/res/drawable/more_vector.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="48dp"
|
||||
android:viewportHeight="144.0"
|
||||
android:viewportWidth="144.0"
|
||||
android:width="48dp">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M72,54m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M72,72m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M72,90m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
|
||||
</vector>
|
||||