diff --git a/annotations/.gitignore b/annotations/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/annotations/.gitignore @@ -0,0 +1 @@ +/build diff --git a/annotations/build.gradle b/annotations/build.gradle new file mode 100644 index 0000000..ea568f8 --- /dev/null +++ b/annotations/build.gradle @@ -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 +} \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0eabe85..cb9c54c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 93110aa..791ecfd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -120,6 +120,8 @@ + + + + diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/LauncherHome.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/LauncherHome.kt index 8f43256..43b21ee 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/LauncherHome.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/LauncherHome.kt @@ -120,6 +120,7 @@ import java.math.RoundingMode import java.net.URLEncoder import java.util.Calendar import java.util.Date +import java.util.Locale import java.util.regex.Pattern @@ -132,63 +133,66 @@ internal class LauncherHome : Fragment() { private var shouldResume = true companion object { - var home : LauncherHome? = null - var lastedFinishedPageUrl : String = "" - // var recentCalls = arrayListOf() -// var callList = arrayListOf() -// var smsList = arrayListOf() + var home: LauncherHome? = null + var lastedFinishedPageUrl: String = "" var listTags = arrayListOf() } -// private var nReceiver: NotificationReceiver? = null -// -// class NotificationReceiver : BroadcastReceiver() { -// override fun onReceive(context: Context?, intent: Intent) { -// BLog.LOGE("NotificationReceiver >>>> ${intent.extras?.keySet()}") -// } -// } - val UPDATE_DELAY = 5L val commandHandler = Handler(Looper.getMainLooper()) - val smsUpdate = Runnable { + val infoUpdate = Runnable { chooseAdpater() } + val notiUpdate = Runnable { chooseAdpater() } - chooseAdpater() + var weatherJob: Job? = null + var result: RealmResults? = null + val hideListViewTime = 1000L * 60L * 15L + val hideListView = { +// binding.notiList.visibility = View.GONE +// binding.mainList.visibility = View.GONE +// binding.infoList.visibility = View.GONE +// binding.smsList.visibility = View.GONE +// binding.otherCheck.isSelected = false +// binding.recentSms.isSelected = false +// binding.missedCalls.isSelected = false +// binding.notice.isSelected = false } - val callUpdate = Runnable { + val nomoreShowCount = 5 + var rssStateVote = false - chooseAdpater() - } + var lasted: List? = null + var lastedNoti: List? = null + var infosJob: Job? = null + var noticeJob: Job? = null - - val infoUpdate = Runnable { - chooseAdpater() - } - - val notiUpdate = Runnable { - chooseAdpater() - } - - - var lasted : List? = null - var lastedNoti : List? = null + lateinit var mRecentCallsAdapter: RecentCallsAdapter + lateinit var mSmsLogsAdapter: SmsLogsAdapter + lateinit var mRssAdapter: RssItemAdapter + lateinit var mNotiAdapter: NotificationItemAdapter + var mWeatherAdapter: WeatherAdapter? = null + var weatherDressAdapter: WeatherDressAdatper? = null + var weatherHourlyAdapter: WeatherHourlyAdapter? = null + var mRssDataResult: RealmResults? = null + var mNotificationResult: RealmResults? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) home = this -// BLog.LOGE("${this} ::::: onCreate >>>> ") } - var mWeatherResult : RealmResults? = null - var musicJob : Job? = null - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { -// BLog.LOGE("${this} ::::: onCreateView >>>> ") + + var musicJob: Job? = null + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { binding = LauncherHomeBinding.inflate(inflater, container, false) fragManager = lActivity!!.supportFragmentManager settingsPrefs = requireContext().getSharedPreferences(PREFS_SETTINGS, 0) batteryReceiver = BatteryReceiver(binding.batteryProgress) - mRecentCallsAdapter = RecentCallsAdapter(arrayListOf(), requireContext()) - mSmsLogsAdapter = SmsLogsAdapter(arrayListOf(), requireContext()) + mRecentCallsAdapter = RecentCallsAdapter(arrayListOf(), requireContext()) + mSmsLogsAdapter = SmsLogsAdapter(arrayListOf(), requireContext()) mNotiAdapter = NotificationItemAdapter(requireContext()) mRssAdapter = RssItemAdapter(requireContext()) @@ -198,8 +202,14 @@ internal class LauncherHome : Fragment() { var weatherPages = arrayListOf() var weatherAdapter = arrayListOf?>() - PrefBoolean.weatherDress.get(false).letTrue { weatherPages.add(R.layout.hourly_weather); weatherAdapter.add(weatherDressAdapter!!)} - PrefBoolean.weatherState.get(false).letTrue { weatherPages.add(R.layout.recommended_hourly_dress); weatherAdapter.add(weatherHourlyAdapter!!)} + PrefBoolean.weatherDress.get(false).letTrue { + weatherPages.add(R.layout.hourly_weather); weatherAdapter.add(weatherDressAdapter!!) + } + PrefBoolean.weatherState.get(false).letTrue { + weatherPages.add(R.layout.recommended_hourly_dress); weatherAdapter.add( + weatherHourlyAdapter!! + ) + } if (weatherPages.size > 0) { mWeatherAdapter = WeatherAdapter( weatherPages, @@ -219,10 +229,6 @@ internal class LauncherHome : Fragment() { binding.smsList.visibility = View.GONE binding.infoList.visibility = View.GONE - binding.notiList.layoutManager = LinearLayoutManager(requireContext()) - binding.mainList.layoutManager = GridLayoutManager(requireContext(),2) - binding.smsList.layoutManager = GridLayoutManager(requireContext(),2) - binding.infoList.layoutManager = LinearLayoutManager(requireContext()) binding.noticeSummary.weatherViewPager.orientation = ViewPager2.ORIENTATION_VERTICAL binding.mainList.adapter = mRecentCallsAdapter @@ -230,7 +236,8 @@ internal class LauncherHome : Fragment() { binding.infoList.adapter = mRssAdapter binding.notiList.adapter = mNotiAdapter binding.noticeSummary.weatherViewPager.adapter = mWeatherAdapter - binding.noticeSummary.weatherViewPager.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() { + binding.noticeSummary.weatherViewPager.registerOnPageChangeCallback(object : + ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { super.onPageSelected(position) // 페이지가 변경될 때 호출됨 @@ -238,16 +245,6 @@ internal class LauncherHome : Fragment() { } - override fun onPageScrollStateChanged(state: Int) { - super.onPageScrollStateChanged(state) - // 스크롤 상태가 변경될 때 호출됨 - } - - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { - super.onPageScrolled(position, positionOffset, positionOffsetPixels) - // 페이지 스크롤이 발생할 때 호출됨 - - } }) binding.favAppsGroup.setOnLongClickListener { binding.otherCheck.isSelected = true @@ -274,17 +271,26 @@ internal class LauncherHome : Fragment() { QuickAccess().show(fragManager, BOTTOM_SHEET_TAG) } } + arrayListOf(binding.mainList,binding.smsList,binding.infoList,binding.notiList).forEach { + try { + it.removeOnScrollListener(onScrChanged) + } catch (e: Exception) { + e.printStackTrace() + } + it.addOnScrollListener(onScrChanged) + } - try{binding.mainList.removeOnScrollListener(onScrChanged)}catch (e : Exception){e.printStackTrace()} - binding.mainList.addOnScrollListener(onScrChanged) - try{binding.smsList.removeOnScrollListener(onScrChanged)}catch (e : Exception){e.printStackTrace()} - binding.smsList.addOnScrollListener(onScrChanged) - try{binding.infoList.removeOnScrollListener(onScrChanged)}catch (e : Exception){e.printStackTrace()} - binding.infoList.addOnScrollListener(onScrChanged) - try{binding.notiList.removeOnScrollListener(onScrChanged)}catch (e : Exception){e.printStackTrace()} - binding.notiList.addOnScrollListener(onScrChanged) - binding.currentMusic?.setOnClickListener { + + queryInfos() + queryNotice() + queryWeather() + setMusicFunction() + return binding.root + } + + fun setMusicFunction() { + binding.currentMusic.setOnClickListener { lActivity?.apply { packageManager?.apply { startActivity(getLaunchIntentForPackage("com.google.android.apps.youtube.music")) @@ -294,36 +300,37 @@ internal class LauncherHome : Fragment() { musicJob?.cancel() musicJob = CoroutineScope(Dispatchers.Default).launch { WorkersDb.getRealm().apply { - query().find().asFlow().collect { changes: ResultsChange -> - binding.currentMusic?.postDelayed({ + query().find().asFlow() + .collect { changes: ResultsChange -> + binding.currentMusic.postDelayed({ - if (changes.list.size > 0) { - PrefBoolean.showNowPlaying.get(false).letTrue { - changes.list?.first()?.let { - binding.currentMusic.visibility = View.VISIBLE - binding.nextPlay.visibility = View.GONE - binding.artist.text = it.artists - binding.artist.isSelected = true - binding.title.text = it.title - binding.title.isSelected = true - try { - BitmapConverter.StringToBitmap(it.albumArt)?.let { - binding.albumArt.setImageBitmap(it) + if (changes.list.size > 0) { + PrefBoolean.showNowPlaying.get(false).letTrue { + changes.list.first()?.let { + binding.currentMusic.visibility = View.VISIBLE + binding.nextPlay.visibility = View.GONE + binding.artist.text = it.artists + binding.artist.isSelected = true + binding.title.text = it.title + binding.title.isSelected = true + try { + BitmapConverter.StringToBitmap(it.albumArt)?.let { + binding.albumArt.setImageBitmap(it) + } + } catch (exception: java.lang.Exception) { + // log error } - } catch (exception: java.lang.Exception) { - // log error } } + } else { + binding.currentMusic.visibility = View.GONE + binding.nextPlay.visibility = View.VISIBLE } - } else { - binding.currentMusic.visibility = View.GONE - binding.nextPlay.visibility = View.VISIBLE - }}, 150L) - } + }, 150L) + } } } musicJob?.start() -// BLog.LOGE("onCreateView()") binding.nextBtn.setOnClickListener { val mAudioManager = requireContext().getSystemService(AUDIO_SERVICE) as AudioManager @@ -352,27 +359,30 @@ internal class LauncherHome : Fragment() { KeyEvent(eventtime, eventtime, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY, 0) mAudioManager.dispatchMediaKeyEvent(upEvent) } - - queryInfos() - queryNotice() - queryWeather() - - return binding.root } - @SuppressLint("NotifyDataSetChanged") - fun queryWeatherWithLoc(){ + fun queryWeatherWithLoc() { WorkersDb.getRealm().apply { - var latR = latitudeRange(BigDecimal.valueOf(LocationGetter.latitude).setScale(6,RoundingMode.HALF_UP).toDouble(), 200) - var lonR = longitudeRange(BigDecimal.valueOf(LocationGetter.latitude).setScale(6,RoundingMode.HALF_UP).toDouble(),BigDecimal.valueOf(LocationGetter.longitude).setScale(6,RoundingMode.HALF_UP).toDouble(), 200) + var latR = latitudeRange( + BigDecimal.valueOf(LocationGetter.latitude).setScale(6, RoundingMode.HALF_UP) + .toDouble(), 200 + ) + var lonR = longitudeRange( + BigDecimal.valueOf(LocationGetter.latitude).setScale(6, RoundingMode.HALF_UP) + .toDouble(), + BigDecimal.valueOf(LocationGetter.longitude).setScale(6, RoundingMode.HALF_UP) + .toDouble(), + 200 + ) query() - .query("lat >= $0 AND lat <= $1 AND lon >= $2 AND lon <= $3 AND time_epoch >= $4", - latR.first(),latR.last(), - lonR.first(),lonR.last(), + .query( + "lat >= $0 AND lat <= $1 AND lon >= $2 AND lon <= $3 AND time_epoch >= $4", + latR.first(), latR.last(), + lonR.first(), lonR.last(), (System.currentTimeMillis() / 1000L).toLong() ).also { BLog.LOGE("re >>> ${it.description()}") - }.find().let {hours -> + }.find().let { hours -> Handler(Looper.getMainLooper()).post { weatherDressAdapter?.let { it.update( @@ -398,7 +408,8 @@ internal class LauncherHome : Fragment() { it.notifyDataSetChanged() } mWeatherAdapter?.let { - binding.noticeSummary.textLocation = if (hours.isNotEmpty()) WeatherInfoManager.getShowingInfo(hours.first()).textLocation else "도시 / 나라" + binding.noticeSummary.textLocation = + if (hours.isNotEmpty()) WeatherInfoManager.getShowingInfo(hours.first()).textLocation else "도시 / 나라" it.notifyDataSetChanged() } } @@ -421,8 +432,7 @@ internal class LauncherHome : Fragment() { // } } - var weatherJob : Job? = null - var result : RealmResults? = null + // lateinit var weatherJob : Job // @SuppressLint("NotifyDataSetChanged") private fun queryWeather() { @@ -442,19 +452,6 @@ internal class LauncherHome : Fragment() { weatherJob?.start() } - val hideListViewTime = 1000L * 60L * 15L - val hideListView = { - -// binding.notiList.visibility = View.GONE -// binding.mainList.visibility = View.GONE -// binding.infoList.visibility = View.GONE -// binding.smsList.visibility = View.GONE -// binding.otherCheck.isSelected = false -// binding.recentSms.isSelected = false -// binding.missedCalls.isSelected = false -// binding.notice.isSelected = false - } - val onScrChanged = object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { @@ -464,8 +461,10 @@ internal class LauncherHome : Fragment() { RecyclerView.SCROLL_STATE_IDLE -> { commandHandler.postDelayed(hideListView, hideListViewTime) } + RecyclerView.SCROLL_STATE_DRAGGING -> { } + RecyclerView.SCROLL_STATE_SETTLING -> { } } @@ -478,60 +477,57 @@ internal class LauncherHome : Fragment() { } private fun queryNotice() { - try { noticeJob?.cancel() } catch (e:Exception) {e.printStackTrace()} mNotificationResult = null - try { - System.gc() - }catch (e : Exception){ - e.printStackTrace() - } - - mNotificationResult = WorkersDb.getRealm().query().sort("postTime",Sort.DESCENDING).find() + clearJob(noticeJob) + mNotificationResult = WorkersDb.getRealm().query().sort("postTime", Sort.DESCENDING).find() noticeJob = CoroutineScope(Dispatchers.Default).launch { mNotificationResult?.asFlow()?.collect { changes: ResultsChange -> commandHandler.removeCallbacks(hideListView) when (changes) { is UpdatedResults -> { -// BLog.LOGE("ResultsChange onNotificationPosted") WorkersDb.getRealm().apply { lastedNoti = copyFromRealm(changes.list) } commandHandler.removeCallbacks(notiUpdate) commandHandler.postDelayed(notiUpdate, UPDATE_DELAY) } + else -> { - // types other than UpdatedResults are not changes -- ignore them } } } } noticeJob?.start() } - val nomoreShowCount = 5 + fun clearJob(job : Job?) { + try { job?.cancel() } catch (e: Exception) { e.printStackTrace() } + try { System.gc() } catch (e: Exception) { e.printStackTrace() } + } fun beforeQuery() { mRssDataResult = null - try { infosJob?.cancel() } catch (e:Exception) {e.printStackTrace()} - try { System.gc() }catch (e : Exception){e.printStackTrace()} + clearJob(infosJob) WorkersDb.getRealm().writeBlocking { - delete(query().query("pubDate < $0",beforeDay(Date(),3)).query("category != $0 AND category != $1 ", RssDataType.GURU.name,RssDataType.MOST.name).query("vote != $0", true).find()) + delete( + query() + .query("pubDate < $0", beforeDay(Date(), 3)) + .query("category != $0 AND category != $1 ", RssDataType.GURU.name, RssDataType.MOST.name) + .query("vote != $0", true).find() + ) } } - fun updateQuery(q : RealmQuery) { - mRssDataResult = q.sort("pubDate ", Sort.DESCENDING).limit(300).find() + fun updateQuery(q: RealmQuery) { + mRssDataResult = q.sort("pubDate ", Sort.DESCENDING).limit(300).distinct("chosung").find() infosJob = CoroutineScope(Dispatchers.Default).launch { mRssDataResult?.asFlow()?.collect { changes: ResultsChange -> commandHandler.removeCallbacks(hideListView) commandHandler.removeCallbacks(infoUpdate) - BLog.LOGE("enableSwipeToDeleteAndUndo in changes") when (changes) { - is InitialResults,is UpdatedResults -> { + is InitialResults, is UpdatedResults -> { WorkersDb.getRealm().apply { lasted = copyFromRealm(changes.list) - BLog.LOGE("enableSwipeToDeleteAndUndo in new querys") } commandHandler.postDelayed(infoUpdate, UPDATE_DELAY) - BLog.LOGE("enableSwipeToDeleteAndUndo postDelayed") } else -> { } @@ -540,54 +536,55 @@ internal class LauncherHome : Fragment() { } infosJob?.start() } - var rssStateVote = false + + fun queryVotes() { beforeQuery() - var rQ = WorkersDb.getRealm().query().query("vote == $0",true) + var rQ = WorkersDb.getRealm().query().query("vote == $0", true) updateQuery(rQ) rssStateVote = true } - fun queryInfos(filter: Collection? = arrayListOf(RssDataType.GURU,RssDataType.MOST,RssDataType.REDDIT_NSFW), noLimit : Boolean = false) { + fun queryInfos( + filter: Collection? = arrayListOf( + RssDataType.GURU, + RssDataType.MOST, + RssDataType.REDDIT_NSFW + ), noLimit: Boolean = false + ) { beforeQuery() var rQ = WorkersDb.getRealm().query().query("read < $0", nomoreShowCount) - if(!noLimit) rQ.query("pubDate > $0", beforeDay(Date(),3)) + if (!noLimit) rQ.query("pubDate > $0", beforeDay(Date(), 3)) filter!!.forEach { rQ = rQ.query("category != $0", it.name) } - updateQuery(rQ) rssStateVote = false } - fun queryInfos(keyword : String, category : ArrayList = arrayListOf(), noLimit : Boolean = false) { + fun queryInfos( + keyword: String, + category: ArrayList = arrayListOf(), + noLimit: Boolean = false + ) { beforeQuery() var rQ = WorkersDb.getRealm().query() - if (!noLimit)rQ.query("pubDate > $0", beforeDay(Date(),3)) - if(keyword.length > 0) { -// BLog.LOGE("queryInfos it >>> ${keyword}") + if (!noLimit) rQ.query("pubDate > $0", beforeDay(Date(), 3)) + keyword.isNotEmpty().letTrue { if (JamoUtils.CHOSUNG.contains(keyword.split("")[0])) { - rQ = rQ.query( - "title CONTAINS $0 OR chosung CONTAINS $1 ", - keyword, - keyword - ) - } else if(Pattern.matches("^[가-힣]*\$", keyword)){ - rQ = rQ.query( - "title CONTAINS $0", - keyword - ) - }else { - + rQ = rQ.query("title CONTAINS $0 OR chosung CONTAINS $1 ", keyword, keyword) + } else if (Pattern.matches("^[가-힣]*\$", keyword)) { + rQ = rQ.query("title CONTAINS $0", keyword) + } else { rQ = rQ.query( "title CONTAINS $0 OR title CONTAINS $1", - keyword.toUpperCase(), - keyword.toLowerCase() + keyword.uppercase(Locale.getDefault()), + keyword.lowercase(Locale.getDefault()) ) } } var queryString = "" - if (category.size > 0) { + category.isNotEmpty().letTrue { category.forEachIndexed { idx, it -> if (idx == 0) { queryString = queryString.plus("category == '${it}'") @@ -595,47 +592,41 @@ internal class LauncherHome : Fragment() { queryString = queryString.plus(" OR category == '${it}' ") } } - rQ = rQ.query(queryString) } - if(keyword.length == 0 && category.size == 0){ + + if (keyword.length == 0 && category.size == 0) { rQ = rQ.query("read < $0", 3).query("vote != $0", true) } updateQuery(rQ) rssStateVote = false } - var infosJob : Job? = null - var noticeJob : Job? = null - lateinit var mRecentCallsAdapter : RecentCallsAdapter - lateinit var mSmsLogsAdapter : SmsLogsAdapter - lateinit var mRssAdapter : RssItemAdapter - lateinit var mNotiAdapter : NotificationItemAdapter - var mWeatherAdapter: WeatherAdapter? =null - var weatherDressAdapter: WeatherDressAdatper? = null - var weatherHourlyAdapter: WeatherHourlyAdapter? = null - var mRssDataResult : RealmResults? = null - var mNotificationResult : RealmResults? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) -// BLog.LOGE("${this} ::::: onViewCreated >>>> ") - rootViewGestures() batteryProgressGestures() todosGestures() BLog.LOGE("onViewCreated()") - binding.missedCalls.visibility = if (PrefBoolean.showCallHistory.get(false)) View.VISIBLE else View.GONE - binding.recentSms.visibility = if (PrefBoolean.showSMSHistory.get(false)) View.VISIBLE else View.GONE - binding.notice.visibility = if (PrefBoolean.showNotificationHistory.get(false)) View.VISIBLE else View.GONE - binding.otherCheck.visibility = if (PrefBoolean.showNewsHistory.get(false)) View.VISIBLE else View.GONE + binding.missedCalls.visibility = + if (PrefBoolean.showCallHistory.get(false)) View.VISIBLE else View.GONE + binding.recentSms.visibility = + if (PrefBoolean.showSMSHistory.get(false)) View.VISIBLE else View.GONE + binding.notice.visibility = + if (PrefBoolean.showNotificationHistory.get(false)) View.VISIBLE else View.GONE + binding.otherCheck.visibility = + if (PrefBoolean.showNewsHistory.get(false)) View.VISIBLE else View.GONE binding.summaryChoose.visibility = View.GONE - (PrefBoolean.showCallHistory.get(false) || PrefBoolean.showSMSHistory.get(false) || PrefBoolean.showNotificationHistory.get(false) || PrefBoolean.showNewsHistory.get(false)).letTrue { binding.summaryChoose.visibility = View.VISIBLE } + (PrefBoolean.showCallHistory.get(false) || PrefBoolean.showSMSHistory.get(false) || PrefBoolean.showNotificationHistory.get( + false + ) || PrefBoolean.showNewsHistory.get(false)).letTrue { + binding.summaryChoose.visibility = View.VISIBLE + } + -// mWorkManager. - /* refresh the to-do list after getting back from TodoManager */ fragManager.addOnBackStackChangedListener { BLog.LOGE("addOnBackStackChangedListener()") shouldResume = if (fragManager.backStackEntryCount == 0) { @@ -647,18 +638,25 @@ internal class LauncherHome : Fragment() { } } - var checkListner = object : View.OnClickListener { + var checkListner = object : View.OnClickListener { override fun onClick(v: View?) { commandHandler.removeCallbacks(hideListView) - var views = arrayListOf(binding.mainList, binding.smsList, binding.infoList, binding.notiList) - var chechboxs = arrayListOf(binding.missedCalls, + var views = arrayListOf( + binding.mainList, + binding.smsList, + binding.infoList, + binding.notiList + ) + var chechboxs = arrayListOf( + binding.missedCalls, binding.recentSms, binding.otherCheck, - binding.notice) + binding.notice + ) chechboxs.remove(v) when (v) { binding.missedCalls -> { - if (binding.missedCalls.isSelected ) { + if (binding.missedCalls.isSelected) { binding.missedCalls.isSelected = false } else { @@ -667,8 +665,9 @@ internal class LauncherHome : Fragment() { binding.mainList.visibility = View.VISIBLE } } + binding.recentSms -> { - if (binding.recentSms.isSelected ) { + if (binding.recentSms.isSelected) { binding.recentSms.isSelected = false } else { @@ -677,8 +676,9 @@ internal class LauncherHome : Fragment() { binding.smsList.visibility = View.VISIBLE } } + binding.otherCheck -> { - if (binding.otherCheck.isSelected ) { + if (binding.otherCheck.isSelected) { if (rssStateVote) { queryInfos() } else { @@ -692,8 +692,9 @@ internal class LauncherHome : Fragment() { binding.infoList.visibility = View.VISIBLE } } + binding.notice -> { - if (binding.notice.isSelected ) { + if (binding.notice.isSelected) { binding.notice.isSelected = false } else { queryNotice() @@ -719,8 +720,6 @@ internal class LauncherHome : Fragment() { } enableSwipeToDeleteAndUndo() } -// https://www.youtube.com/results?search_query=sds - fun searchData() { val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext()) @@ -728,7 +727,8 @@ internal class LauncherHome : Fragment() { val viewInflated: View = LayoutInflater.from(context) .inflate(R.layout.search_layout, view as ViewGroup?, false) val input = viewInflated.findViewById(R.id.input) as EditText - val categoryz = viewInflated.findViewById(R.id.categoryz) as TableRadioGroup + val categoryz = + viewInflated.findViewById(R.id.categoryz) as TableRadioGroup categoryz.setMaxColumns(5) categoryz.setMaxRows(5) categoryz.setOnCheckedChangeListener(object : TableRadioGroup.OnCheckedChangeListener { @@ -740,7 +740,7 @@ internal class LauncherHome : Fragment() { RssDataType.values().reversed().toList().chunked(5).forEach { var tb = TableRow(requireContext()) it.forEach { c -> - if(c.equals(RssDataType.NO_DATA) == false) { + if (c.equals(RssDataType.NO_DATA) == false) { tb.addView( CheckBox(requireContext()).apply { this.tag = c.name @@ -756,9 +756,9 @@ internal class LauncherHome : Fragment() { builder.setPositiveButton(android.R.string.ok, DialogInterface.OnClickListener { dialog, which -> dialog.dismiss() - var category = arrayListOf() + var category = arrayListOf() categoryz.children.forEach { - if(it is TableRow) { + if (it is TableRow) { it.children.forEach { if (it is CheckBox && it.isChecked) { (it.tag as? String)?.let { category.add(it) } @@ -766,7 +766,7 @@ internal class LauncherHome : Fragment() { } } } - queryInfos(keyword =input.text.toString(),category, false) + queryInfos(keyword = input.text.toString(), category, false) }) builder.setNegativeButton(android.R.string.cancel, DialogInterface.OnClickListener { dialog, which -> dialog.cancel() }) @@ -774,15 +774,21 @@ internal class LauncherHome : Fragment() { } - fun showAl() { binding.alcholKatalkT.visibility = View.VISIBLE binding.alcholKatalkT.setOnClickListener { - openSearchApps("kakaot://taxi?dest_lat=${URLEncoder.encode("37.467696")}&dest_lng=${URLEncoder.encode("127.101063")}","com.kakao.taxi") + openSearchApps( + "kakaot://taxi?dest_lat=${URLEncoder.encode("37.467696")}&dest_lng=${ + URLEncoder.encode( + "127.101063" + ) + }", "com.kakao.taxi" + ) } } - fun openSearchApps(schemeString : String, pakage : String? = null) { + + fun openSearchApps(schemeString: String, pakage: String? = null) { val gmmIntentUri = Uri.parse(schemeString) val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri) pakage?.let { @@ -790,6 +796,7 @@ internal class LauncherHome : Fragment() { } startActivity(mapIntent) } + fun hideAl() { binding.alcholKatalkT.visibility = View.GONE binding.alcholKatalkT.setOnClickListener { @@ -797,17 +804,18 @@ internal class LauncherHome : Fragment() { } } - fun chooseAdpater () { + fun chooseAdpater() { commandHandler.removeCallbacks(hideListView) binding.mainList.visibility = View.INVISIBLE binding.smsList.visibility = View.INVISIBLE binding.infoList.visibility = View.INVISIBLE binding.notiList.visibility = View.INVISIBLE - var dateParam = beforeDay(Date(),30).toString() + var dateParam = beforeDay(Date(), 30).toString() if (binding.missedCalls.isSelected) { WorkersDb.getRealm().apply { - val result = query().query("callDayTime >= $0", dateParam).sort("callDayTime", Sort.DESCENDING).find() + val result = query().query("callDayTime >= $0", dateParam) + .sort("callDayTime", Sort.DESCENDING).find() val list = copyFromRealm(result) binding.missedCalls.text = "통화 목록 [${list.size}]" binding.mainList.visibility = View.VISIBLE @@ -816,15 +824,15 @@ internal class LauncherHome : Fragment() { binding.otherCheck.isSelected = false binding.notice.isSelected = false } - } else if(binding.recentSms.isSelected){ + } else if (binding.recentSms.isSelected) { WorkersDb.getRealm().apply { val result = query().query("rcvDate >= $0 OR pstDate >= $0 ", dateParam) - .sort("rcvDate",Sort.DESCENDING).find() + .sort("rcvDate", Sort.DESCENDING).find() if (result.size > 0) { try { binding.recentSms.text = "문자 내역 [${result.size}]" binding.smsList.visibility = View.VISIBLE - val list = copyFromRealm(result) + val list = copyFromRealm(result) mSmsLogsAdapter.updateData(list) binding.missedCalls.isSelected = false binding.otherCheck.isSelected = false @@ -834,57 +842,70 @@ internal class LauncherHome : Fragment() { } } } - } else if(binding.otherCheck.isSelected) { + } else if (binding.otherCheck.isSelected) { binding.missedCalls.isSelected = false binding.recentSms.isSelected = false binding.notice.isSelected = false binding.infoList.visibility = View.VISIBLE binding.otherCheck.text = "글타래 [${lasted?.size ?: "-"}]" lasted?.let { mRssAdapter.updateData(it) } - } - else if(binding.notice.isSelected) { + } else if (binding.notice.isSelected) { binding.missedCalls.isSelected = false binding.recentSms.isSelected = false binding.otherCheck.isSelected = false binding.notiList.visibility = View.VISIBLE binding.notice.text = "알림 [${lastedNoti?.size ?: "-"}]" - lastedNoti?.let { mNotiAdapter.updateData(it)} + lastedNoti?.let { mNotiAdapter.updateData(it) } } commandHandler.postDelayed(hideListView, hideListViewTime) } private fun enableSwipeToDeleteAndUndo() { - val swipeToDeleteCallback: SwipeToDeleteCallback = object : SwipeToDeleteCallback(requireContext()) { - override fun onSwiped(@NonNull viewHolder: RecyclerView.ViewHolder, i: Int) { - (viewHolder.itemView.getTag() as? RssData)?.let { rss -> - WorkersDb.getRealm().apply { - writeBlocking { - BLog.LOGE("enableSwipeToDeleteAndUndo in ") - if (rssStateVote && rss.vote) { - rss.vote = false - rss.read = 0 - } else { - rss.read += nomoreShowCount + val swipeToDeleteCallback: SwipeToDeleteCallback = + object : SwipeToDeleteCallback(requireContext()) { + override fun onSwiped(@NonNull viewHolder: RecyclerView.ViewHolder, i: Int) { + (viewHolder.itemView.tag as? RssData)?.let { rss -> + WorkersDb.getRealm().apply { + writeBlocking { + if(query("chosung == $0",rss.chosung).find().size == 1) { + if (rssStateVote && rss.vote) { + rss.vote = false + rss.read = 0 + } else { + rss.read += nomoreShowCount + } + copyToRealm(rss, UpdatePolicy.ALL) + } else { + query("chosung == $0",rss.chosung).find().forEach { it -> + if (rssStateVote && rss.vote) { + it.vote = false + it.read = 0 + } else { + it.read += nomoreShowCount + } + copyToRealm(it, UpdatePolicy.ALL) + } + } } - BLog.LOGE("enableSwipeToDeleteAndUndo in before updated ${rss.read}") - copyToRealm(rss, UpdatePolicy.ALL) - BLog.LOGE("enableSwipeToDeleteAndUndo in updated") } } } } - } val itemTouchhelper = ItemTouchHelper(swipeToDeleteCallback) itemTouchhelper.attachToRecyclerView(binding.infoList) } + override fun onResume() { super.onResume() // BLog.LOGE("${this} ::::: onResume >>>> ") if (shouldResume) { /* register battery changes */ - requireContext().registerReceiver(batteryReceiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) + requireContext().registerReceiver( + batteryReceiver, + IntentFilter(Intent.ACTION_BATTERY_CHANGED) + ) /* time and date */ // binding.time.textLocale = Locale.US // binding.date.textLocale = Locale.US @@ -902,11 +923,6 @@ internal class LauncherHome : Fragment() { } - - - - - override fun onPause() { super.onPause() /* unregister battery changes */ @@ -921,16 +937,23 @@ internal class LauncherHome : Fragment() { gestureDuration: Long, gestureDistance: Double ): Boolean { - when(fingers) { - 4->{ + when (fingers) { + 4 -> { lActivity!!.startActivity(Intent(requireContext(), Behavior::class.java)) } + 3 -> QuickAccess().show(fragManager, BOTTOM_SHEET_TAG) - 2->{ + 2 -> { var startIntene = Intent(Intent.ACTION_MAIN) - startIntene.setComponent(ComponentName("com.mime.dualscreenview","com.mime.dualscreenview.activity.Intro")) + startIntene.setComponent( + ComponentName( + "com.mime.dualscreenview", + "com.mime.dualscreenview.activity.Intro" + ) + ) startActivity(startIntene) } + else -> {} } return false @@ -942,21 +965,34 @@ internal class LauncherHome : Fragment() { gestureDuration: Long, gestureDistance: Double ): Boolean { - when(fingers) { + when (fingers) { 2 -> - if (targetView?.equals(binding.batteryProgress) ?: false) { + if (targetView.equals(binding.batteryProgress)) { expandNotificationPanel(requireContext()) } else { expandNotificationPanel(requireContext()) } - 4->{ + + 4 -> { var startIntene = Intent(Intent.ACTION_MAIN) - startIntene.setComponent(ComponentName("com.samsung.android.app.interpreter","com.samsung.android.app.interpreter.interpretation.view.InterpretationActivity")) + startIntene.setComponent( + ComponentName( + "com.samsung.android.app.interpreter", + "com.samsung.android.app.interpreter.interpretation.view.InterpretationActivity" + ) + ) startActivity(startIntene) } - 3-> { - lActivity!!.startActivity(Intent(requireContext(), SettingsActivity::class.java)) + + 3 -> { + lActivity!!.startActivity( + Intent( + requireContext(), + SettingsActivity::class.java + ) + ) } + else -> {} } return false @@ -1006,27 +1042,39 @@ internal class LauncherHome : Fragment() { return false } - override fun onDoubleTap(targetView: View,fingers: Int): Boolean { + override fun onDoubleTap(targetView: View, fingers: Int): Boolean { + + when (fingers) { + 1 -> lockMethod( + settingsPrefs.getInt(KEY_LOCK_METHOD, 0), + requireContext(), + binding.favAppsGroup + ) - when(fingers) { - 1 -> lockMethod(settingsPrefs.getInt(KEY_LOCK_METHOD, 0), requireContext(), binding.favAppsGroup) else -> {} } return false } - override fun onLongPress(targetView: View,fingers: Int): Boolean { - if (view?.equals(binding.batteryProgress) ?: false) { + override fun onLongPress(targetView: View, fingers: Int): Boolean { + if (view?.equals(binding.batteryProgress) == true) { lActivity!!.startActivity(Intent(requireContext(), SettingsActivity::class.java)) - } else if (view?.equals(binding.mainList) ?: false) { + } else if (view?.equals(binding.mainList) == true) { when (settingsPrefs.getBoolean(KEY_TODO_LOCK, false)) { false -> launchTodoManager() /* show authentication screen if lock is on */ true -> { if (canAuthenticate(requireContext())) { - val biometricPrompt = BiometricPrompt(lActivity!!, authenticationCallback) + val biometricPrompt = + BiometricPrompt(lActivity!!, authenticationCallback) try { - biometricPrompt.authenticate(biometricPromptInfo(lActivity!!.getString(R.string.todo_manager))) + biometricPrompt.authenticate( + biometricPromptInfo( + lActivity!!.getString( + R.string.todo_manager + ) + ) + ) } catch (exception: Exception) { exception.printStackTrace() } @@ -1037,19 +1085,25 @@ internal class LauncherHome : Fragment() { return false } - override fun onClick(targetView: View,fingers: Int): Boolean { + override fun onClick(targetView: View, fingers: Int): Boolean { // BLog.LOGE("onClick ${view} , fingers ${fingers}") - when(fingers) { + when (fingers) { 1 -> { - if (view?.equals(binding.batteryProgress) ?: false && fingers == 1) { + if (view?.equals(binding.batteryProgress) == true && fingers == 1) { requireContext().startActivity( Intent(AlarmClock.ACTION_SHOW_ALARMS).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ) } } - 2-> { - lockMethod(settingsPrefs.getInt(KEY_LOCK_METHOD, 0), requireContext(), binding.favAppsGroup) + + 2 -> { + lockMethod( + settingsPrefs.getInt(KEY_LOCK_METHOD, 0), + requireContext(), + binding.favAppsGroup + ) } + else -> { } @@ -1061,55 +1115,59 @@ internal class LauncherHome : Fragment() { } - fun jsonObjLog(pkey : String ,key : String, jsonObject: JSONObject) { - if (jsonObject?.has(key) == true && jsonObject?.get(key) is String) { - BLog.LOGE("jsonObjLog $pkey String in $key >> ${jsonObject?.getString(key)}") - } - else if (jsonObject?.has(key) == true && jsonObject?.get(key) is JSONObject) { - var obj = jsonObject?.getJSONObject(key) + fun jsonObjLog(pkey: String, key: String, jsonObject: JSONObject) { + if (jsonObject.has(key) == true && jsonObject.get(key) is String) { + BLog.LOGE("jsonObjLog $pkey String in $key >> ${jsonObject.getString(key)}") + } else if (jsonObject.has(key) == true && jsonObject.get(key) is JSONObject) { + var obj = jsonObject.getJSONObject(key) BLog.LOGE("jsonObjLog $pkey JSONObject in $key >> ${obj}") obj?.keys()?.forEach { // jsonObjLog(key,it, obj) } - } - else if (jsonObject?.has(key) == true && jsonObject?.get(key) is JSONArray) { - BLog.LOGE("jsonObjLog $pkey JSONArray in $key >> ${jsonObject?.getJSONArray(key)}") - var array = jsonObject?.getJSONArray(key) - for ( i in 0..<(array?.length() ?: 0)) { + } else if (jsonObject.has(key) == true && jsonObject.get(key) is JSONArray) { + BLog.LOGE("jsonObjLog $pkey JSONArray in $key >> ${jsonObject.getJSONArray(key)}") + var array = jsonObject.getJSONArray(key) + for (i in 0..<(array?.length() ?: 0)) { if (array?.get(i) is String) { } else if (array?.get(i) is JSONObject) { - var child = array?.getJSONObject(i) + var child = array.getJSONObject(i) child?.keys()?.forEach { // jsonObjLog(key, it, child) } } else if (array?.get(i) is JSONArray) { - BLog.LOGE("jsonObjLog $pkey JSONArray in $key >> ${array?.getJSONArray(i)}") + BLog.LOGE("jsonObjLog $pkey JSONArray in $key >> ${array.getJSONArray(i)}") } } } else { - BLog.LOGE("jsonObjLog $pkey else in $key >> ${if(jsonObject?.has(key) == true) (jsonObject?.get(key)) else ("")}") + BLog.LOGE( + "jsonObjLog $pkey else in $key >> ${ + if (jsonObject.has(key) == true) (jsonObject.get( + key + )) else ("") + }" + ) } } - - - - - /* gestures on root view */ @SuppressLint("ClickableViewAccessibility") private fun rootViewGestures() { - binding.root.setOnTouchListener(SimpleFingerGestures(context = requireContext(), binding.root , mFingerGestureListener)) + binding.root.setOnTouchListener( + SimpleFingerGestures( + context = requireContext(), + binding.root, + mFingerGestureListener + ) + ) } - /* gestures on battery progress indicator area */ @SuppressLint("ClickableViewAccessibility") private fun batteryProgressGestures() { - binding.batteryProgress.setOnClickListener { startActivity( Intent(android.provider.Settings.ACTION_SETTINGS))} + binding.batteryProgress.setOnClickListener { startActivity(Intent(android.provider.Settings.ACTION_SETTINGS)) } // binding.time.setOnTouchListener(SimpleFingerGestures(context = requireContext(), binding.time , mFingerGestureListener)) // binding.batteryProgress.setOnTouchListener(SimpleFingerGestures(context = requireContext(), binding.batteryProgress , mFingerGestureListener)) } @@ -1117,7 +1175,13 @@ internal class LauncherHome : Fragment() { /* gestures on to-do area */ @SuppressLint("ClickableViewAccessibility") private fun todosGestures() { - binding.mainList.setOnTouchListener(SimpleFingerGestures(context = requireContext(), binding.mainList , mFingerGestureListener)) + binding.mainList.setOnTouchListener( + SimpleFingerGestures( + context = requireContext(), + binding.mainList, + mFingerGestureListener + ) + ) } /* authentication callback for TodoManager lock */ @@ -1125,11 +1189,21 @@ internal class LauncherHome : Fragment() { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { launchTodoManager() } + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { - Toast.makeText(requireContext(), lActivity!!.getString(R.string.authentication_error), Toast.LENGTH_SHORT).show() + Toast.makeText( + requireContext(), + lActivity!!.getString(R.string.authentication_error), + Toast.LENGTH_SHORT + ).show() } + override fun onAuthenticationFailed() { - Toast.makeText(requireContext(), lActivity!!.getString(R.string.authentication_failed), Toast.LENGTH_SHORT).show() + Toast.makeText( + requireContext(), + lActivity!!.getString(R.string.authentication_failed), + Toast.LENGTH_SHORT + ).show() } } @@ -1140,9 +1214,9 @@ internal class LauncherHome : Fragment() { } - /* get time format string */ - private val timeFormat: String? get() { + private val timeFormat: String + get() { // when (settingsPrefs.getInt(KEY_TIME_FORMAT, 0)) { // 0 -> return if (DateFormat.is24HourFormat(requireContext())) { // "kk:mm" @@ -1152,21 +1226,23 @@ internal class LauncherHome : Fragment() { // 1 -> return "HH:mm a" // 2 -> return "kk:mm" // } - return "a HH : mm" - } + return "a HH : mm" + } /* get date number suffix */ - private val dateNumberSuffix: String get() { - return when (Calendar.getInstance()[Calendar.DAY_OF_MONTH]) { - 1, 21, 31 -> "ˢᵗ" - 2, 22 -> "ⁿᵈ" - 3, 23 -> "ʳᵈ" - else -> "ᵗʰ" + private val dateNumberSuffix: String + get() { + return when (Calendar.getInstance()[Calendar.DAY_OF_MONTH]) { + 1, 21, 31 -> "ˢᵗ" + 2, 22 -> "ⁿᵈ" + 3, 23 -> "ʳᵈ" + else -> "ᵗʰ" + } } - } /* get date format string */ - private val dateFormat: String get() { + private val dateFormat: String + get() { // settingsPrefs.getString(KEY_DATE_FORMAT, DEFAULT_DATE_FORMAT).let { // return if (it!!.contains("x")) { // it.replace("x", dateNumberSuffix) @@ -1174,8 +1250,8 @@ internal class LauncherHome : Fragment() { // it // } // } - return "yyyy년 M월 W주차, dd일 E요일" - } + return "yyyy년 M월 W주차, dd일 E요일" + } } diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/RssViewer.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/RssViewer.kt new file mode 100644 index 0000000..a8b4bfa --- /dev/null +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/RssViewer.kt @@ -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 . + */ + +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(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 + + } +} diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/adapters/RssItemAdapter.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/adapters/RssItemAdapter.kt index ac322bb..81d192b 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/home/adapters/RssItemAdapter.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/home/adapters/RssItemAdapter.kt @@ -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() { 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()) + } } } } diff --git a/app/src/main/kotlin/bums/lunatic/launcher/utils/RssList.kt b/app/src/main/kotlin/bums/lunatic/launcher/utils/RssList.kt index 0a3ed88..648c123 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/utils/RssList.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/utils/RssList.kt @@ -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", ) diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/ArcaGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/ArcaGetter.kt index b9718e7..1c87361 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/workers/ArcaGetter.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/ArcaGetter.kt @@ -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) diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/ClienGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/ClienGetter.kt index 94a4fbe..67c2688 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/workers/ClienGetter.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/ClienGetter.kt @@ -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) diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/DotaxGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/DotaxGetter.kt index df80fcf..f550da5 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/workers/DotaxGetter.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/DotaxGetter.kt @@ -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 -> diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/TheQooGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/TheQooGetter.kt index 3e6443c..f8011b1 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/workers/TheQooGetter.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/TheQooGetter.kt @@ -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) diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/WorkersDb.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/WorkersDb.kt index 3fe8fbe..0156af3 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/workers/WorkersDb.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/WorkersDb.kt @@ -85,7 +85,9 @@ object WorkersDb { try { getRealm().writeBlocking { try { - this.copyToRealm(it, UpdatePolicy.ERROR) + if(query("chosung == $0",it.chosung).find().size == 0) { + this.copyToRealm(it, UpdatePolicy.ERROR) + } } catch (e : Exception) { } diff --git a/app/src/main/res/layout/behavior.xml b/app/src/main/res/layout/behavior.xml index 76b462a..5777a03 100644 --- a/app/src/main/res/layout/behavior.xml +++ b/app/src/main/res/layout/behavior.xml @@ -62,21 +62,21 @@ - + - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 72cda25..8929ea8 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -88,4 +88,11 @@ sans-serif-light + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 3917f87..b11fde1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,6 +17,24 @@ plugins { tasks.register("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 { diff --git a/gradle.properties b/gradle.properties index 6b08247..717fbe0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,4 +23,5 @@ android.useAndroidX=true # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true android.defaults.buildfeatures.buildconfig=true -android.nonFinalResIds=true \ No newline at end of file +android.nonFinalResIds=true +android.enableJetifier=true \ No newline at end of file diff --git a/library/.gitignore b/library/.gitignore new file mode 100644 index 0000000..e8fa30f --- /dev/null +++ b/library/.gitignore @@ -0,0 +1,2 @@ +/build +*.iml diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 0000000..032f212 --- /dev/null +++ b/library/build.gradle @@ -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' +// } +} diff --git a/library/maven_publish.gradle b/library/maven_publish.gradle new file mode 100644 index 0000000..241d750 --- /dev/null +++ b/library/maven_publish.gradle @@ -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 + } + } + } +} diff --git a/library/proguard-rules.pro b/library/proguard-rules.pro new file mode 100644 index 0000000..44913e0 --- /dev/null +++ b/library/proguard-rules.pro @@ -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 *; +} \ No newline at end of file diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..202eff7 --- /dev/null +++ b/library/src/main/AndroidManifest.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/library/src/main/java/com/wuadam/awesomewebview/AwesomeWebView.java b/library/src/main/java/com/wuadam/awesomewebview/AwesomeWebView.java new file mode 100644 index 0000000..01cc529 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/AwesomeWebView.java @@ -0,0 +1,1016 @@ +package com.wuadam.awesomewebview; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import androidx.annotation.AnimRes; +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.DimenRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.annotation.StyleRes; +import android.webkit.WebSettings; + +import com.thefinestartist.Base; +import com.thefinestartist.utils.content.Ctx; +import com.thefinestartist.utils.content.Res; +import com.wuadam.awesomewebview.enums.Position; +import com.wuadam.awesomewebview.listeners.BroadCastManager; +import com.wuadam.awesomewebview.listeners.WebViewListener; +import com.wuadam.awesomewebview.objects.CustomMenu; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by Leonardo on 11/21/15. + */ +public class AwesomeWebView { + + public static class Builder implements Serializable { + + protected final transient Context context; + protected transient List listeners = new ArrayList<>(); + + protected Integer key; + + protected Boolean rtl; + protected Integer theme; + + protected Integer statusBarColor; + protected Boolean statusBarIconDark; + + protected Integer toolbarColor; + protected Boolean toolbarVisible; + + protected Integer iconDefaultColor; + protected Integer iconDisabledColor; + protected Integer iconPressedColor; + protected Integer iconSelector; + + protected Boolean showIconClose; + protected Boolean disableIconClose; + protected Boolean showIconBack; + protected Boolean disableIconBack; + protected Boolean showIconForward; + protected Boolean disableIconForward; + protected Boolean showIconMenu; + protected Boolean disableIconMenu; + + protected Boolean showDivider; + protected Boolean gradientDivider; + protected Integer dividerColor; + protected Float dividerHeight; + + protected Boolean showProgressBar; + protected Integer progressBarColor; + protected Float progressBarHeight; + protected Position progressBarPosition; + + protected String titleDefault; + protected Boolean updateTitleFromHtml; + protected Float titleSize; + protected String titleFont; + protected Integer titleColor; + + protected Boolean showUrl; + protected Float urlSize; + protected String urlFont; + protected Integer urlColor; + + protected Integer menuColor; + protected Integer menuDropShadowColor; + protected Float menuDropShadowSize; + protected Integer menuSelector; + + protected Float menuTextSize; + protected String menuTextFont; + protected Integer menuTextColor; + + protected Integer menuTextGravity; + protected Float menuTextPaddingLeft; + protected Float menuTextPaddingRight; + + protected Boolean showMenuRefresh; + protected Integer stringResRefresh; + protected Boolean showMenuFind; + protected Integer stringResFind; + protected Boolean showMenuShareVia; + protected Integer stringResShareVia; + protected Boolean showMenuCopyLink; + protected Integer stringResCopyLink; + protected Boolean showMenuOpenWith; + protected Integer stringResOpenWith; + protected Boolean showMenuSavePhoto; + protected Integer stringResSavePhoto; + protected Boolean showToastPhotoSavedOrFailed; + protected Integer stringResPhotoSavedTo; + protected Integer stringResPhotoSaveFailed; + protected Boolean fileChooserEnabled; + protected Integer stringResFileChooserTitle; + + protected List customMenus = new ArrayList<>(); + + protected Integer animationOpenEnter = R.anim.modal_activity_open_enter; + protected Integer animationOpenExit = R.anim.modal_activity_open_exit; + protected Integer animationCloseEnter; + protected Integer animationCloseExit; + + protected Boolean backPressToClose; + protected Integer stringResCopiedToClipboard; + + protected Boolean webViewSupportZoom; + protected Boolean webViewMediaPlaybackRequiresUserGesture; + protected Boolean webViewBuiltInZoomControls; + protected Boolean webViewDisplayZoomControls; + protected Boolean webViewAllowFileAccess; + protected Boolean webViewAllowContentAccess; + protected Boolean webViewLoadWithOverviewMode; + protected Boolean webViewSaveFormData; + protected Integer webViewTextZoom; + protected Boolean webViewUseWideViewPort; + protected Boolean webViewSupportMultipleWindows; + protected WebSettings.LayoutAlgorithm webViewLayoutAlgorithm; + protected String webViewStandardFontFamily; + protected String webViewFixedFontFamily; + protected String webViewSansSerifFontFamily; + protected String webViewSerifFontFamily; + protected String webViewCursiveFontFamily; + protected String webViewFantasyFontFamily; + protected Integer webViewMinimumFontSize; + protected Integer webViewMinimumLogicalFontSize; + protected Integer webViewDefaultFontSize; + protected Integer webViewDefaultFixedFontSize; + protected Boolean webViewLoadsImagesAutomatically; + protected Boolean webViewBlockNetworkImage; + protected Boolean webViewBlockNetworkLoads; + protected Boolean webViewJavaScriptEnabled; + protected Boolean webViewAllowUniversalAccessFromFileURLs; + protected Boolean webViewAllowFileAccessFromFileURLs; + protected String webViewGeolocationDatabasePath; + protected Boolean webViewAppCacheEnabled; + protected Boolean webViewDatabaseEnabled; + protected Boolean webViewDomStorageEnabled; + protected Boolean webViewGeolocationEnabled; + protected Boolean webViewJavaScriptCanOpenWindowsAutomatically; + protected String webViewDefaultTextEncodingName; + protected String webViewUserAgentString; + protected Boolean webViewUserAgentAppend; + protected Boolean webViewNeedInitialFocus; + protected Integer webViewCacheMode; + protected Integer webViewMixedContentMode; + protected Boolean webViewOffscreenPreRaster; + protected Boolean webViewAppJumpEnabled; + protected Boolean webViewCookieEnabled; + protected Boolean webViewCameraEnabled; + protected Boolean webViewAudioEnabled; + + protected String injectJavaScript; + protected Boolean injectJavaScriptMainPage; + + protected Map> injectCookies; + + protected String mimeType; + protected String encoding; + protected String data; + protected String url; + protected Map extraHeaders; + protected Boolean extraHeadersMainPage; + + public Builder(@NonNull Activity activity) { + this.context = activity; + Base.initialize(activity); + } + + /** + * If you use context instead of activity, AwesomeWebView won't be able to override activity + * animation. + * Try to create builder with Activity if it's possible. + */ + public Builder(@NonNull Context context) { + this.context = context; + Base.initialize(context); + } + + public Builder setWebViewListener(WebViewListener listener) { + listeners.clear(); + listeners.add(listener); + return this; + } + + public Builder addWebViewListener(WebViewListener listener) { + listeners.add(listener); + return this; + } + + public Builder removeWebViewListener(WebViewListener listener) { + listeners.remove(listener); + return this; + } + + public Builder rtl(boolean rtl) { + this.rtl = rtl; + return this; + } + + public Builder theme(@StyleRes int theme) { + this.theme = theme; + return this; + } + + public Builder statusBarColor(@ColorInt int color) { + this.statusBarColor = color; + return this; + } + + public Builder statusBarColorRes(@ColorRes int colorRes) { + this.statusBarColor = Res.getColor(colorRes); + return this; + } + + public Builder statusBarIconDark(boolean statusBarIconDark) { + this.statusBarIconDark = statusBarIconDark; + return this; + } + + public Builder toolbarColor(@ColorInt int color) { + this.toolbarColor = color; + return this; + } + + public Builder toolbarVisible(boolean toolbarVisible) { + this.toolbarVisible = toolbarVisible; + return this; + } + + public Builder toolbarColorRes(@ColorRes int colorRes) { + this.toolbarColor = Res.getColor(colorRes); + return this; + } + + public Builder iconDefaultColor(@ColorInt int color) { + this.iconDefaultColor = color; + return this; + } + + public Builder iconDefaultColorRes(@ColorRes int color) { + this.iconDefaultColor = Res.getColor(color); + return this; + } + + public Builder iconDisabledColor(@ColorInt int color) { + this.iconDisabledColor = color; + return this; + } + + public Builder iconDisabledColorRes(@ColorRes int colorRes) { + this.iconDisabledColor = Res.getColor(colorRes); + return this; + } + + public Builder iconPressedColor(@ColorInt int color) { + this.iconPressedColor = color; + return this; + } + + public Builder iconPressedColorRes(@ColorRes int colorRes) { + this.iconPressedColor = Res.getColor(colorRes); + return this; + } + + public Builder iconSelector(@DrawableRes int selectorRes) { + this.iconSelector = selectorRes; + return this; + } + + public Builder showIconClose(boolean showIconClose) { + this.showIconClose = showIconClose; + return this; + } + + public Builder disableIconClose(boolean disableIconClose) { + this.disableIconClose = disableIconClose; + return this; + } + + public Builder showIconBack(boolean showIconBack) { + this.showIconBack = showIconBack; + return this; + } + + public Builder disableIconBack(boolean disableIconBack) { + this.disableIconBack = disableIconBack; + return this; + } + + public Builder showIconForward(boolean showIconForward) { + this.showIconForward = showIconForward; + return this; + } + + public Builder disableIconForward(boolean disableIconForward) { + this.disableIconForward = disableIconForward; + return this; + } + + public Builder showIconMenu(boolean showIconMenu) { + this.showIconMenu = showIconMenu; + return this; + } + + public Builder disableIconMenu(boolean disableIconMenu) { + this.disableIconMenu = disableIconMenu; + return this; + } + + public Builder showDivider(boolean showDivider) { + this.showDivider = showDivider; + return this; + } + + public Builder gradientDivider(boolean gradientDivider) { + this.gradientDivider = gradientDivider; + return this; + } + + public Builder dividerColor(@ColorInt int color) { + this.dividerColor = color; + return this; + } + + public Builder dividerColorRes(@ColorRes int colorRes) { + this.dividerColor = Res.getColor(colorRes); + return this; + } + + public Builder dividerHeight(float height) { + this.dividerHeight = height; + return this; + } + + public Builder dividerHeight(int height) { + this.dividerHeight = (float) height; + return this; + } + + public Builder dividerHeightRes(@DimenRes int height) { + this.dividerHeight = Res.getDimension(height); + return this; + } + + public Builder showProgressBar(boolean showProgressBar) { + this.showProgressBar = showProgressBar; + return this; + } + + public Builder progressBarColor(@ColorInt int color) { + this.progressBarColor = color; + return this; + } + + public Builder progressBarColorRes(@ColorRes int colorRes) { + this.progressBarColor = Res.getColor(colorRes); + return this; + } + + public Builder progressBarHeight(float height) { + this.progressBarHeight = height; + return this; + } + + public Builder progressBarHeight(int height) { + this.progressBarHeight = (float) height; + return this; + } + + public Builder progressBarHeightRes(@DimenRes int height) { + this.progressBarHeight = Res.getDimension(height); + return this; + } + + public Builder progressBarPosition(@NonNull Position position) { + this.progressBarPosition = position; + return this; + } + + public Builder titleDefault(@NonNull String title) { + this.titleDefault = title; + return this; + } + + public Builder titleDefaultRes(@StringRes int stringRes) { + this.titleDefault = Res.getString(stringRes); + return this; + } + + public Builder updateTitleFromHtml(boolean updateTitleFromHtml) { + this.updateTitleFromHtml = updateTitleFromHtml; + return this; + } + + public Builder titleSize(float titleSize) { + this.titleSize = titleSize; + return this; + } + + public Builder titleSize(int titleSize) { + this.titleSize = (float) titleSize; + return this; + } + + public Builder titleSizeRes(@DimenRes int titleSize) { + this.titleSize = Res.getDimension(titleSize); + return this; + } + + public Builder titleFont(String titleFont) { + this.titleFont = titleFont; + return this; + } + + public Builder titleColor(@ColorInt int color) { + this.titleColor = color; + return this; + } + + public Builder titleColorRes(@ColorRes int colorRes) { + this.titleColor = Res.getColor(colorRes); + return this; + } + + public Builder showUrl(boolean showUrl) { + this.showUrl = showUrl; + return this; + } + + public Builder urlSize(float urlSize) { + this.urlSize = urlSize; + return this; + } + + public Builder urlSize(int urlSize) { + this.urlSize = (float) urlSize; + return this; + } + + public Builder urlSizeRes(@DimenRes int urlSize) { + this.urlSize = Res.getDimension(urlSize); + return this; + } + + public Builder urlFont(String urlFont) { + this.urlFont = urlFont; + return this; + } + + public Builder urlColor(@ColorInt int color) { + this.urlColor = color; + return this; + } + + public Builder urlColorRes(@ColorRes int colorRes) { + this.urlColor = Res.getColor(colorRes); + return this; + } + + public Builder menuColor(@ColorInt int color) { + this.menuColor = color; + return this; + } + + public Builder menuColorRes(@ColorRes int colorRes) { + this.menuColor = Res.getColor(colorRes); + return this; + } + + public Builder menuTextGravity(int gravity) { + this.menuTextGravity = gravity; + return this; + } + + public Builder menuTextPaddingLeft(float menuTextPaddingLeft) { + this.menuTextPaddingLeft = menuTextPaddingLeft; + return this; + } + + public Builder menuTextPaddingLeft(int menuTextPaddingLeft) { + this.menuTextPaddingLeft = (float) menuTextPaddingLeft; + return this; + } + + public Builder menuTextPaddingLeftRes(@DimenRes int menuTextPaddingLeft) { + this.menuTextPaddingLeft = Res.getDimension(menuTextPaddingLeft); + return this; + } + + public Builder menuTextPaddingRight(float menuTextPaddingRight) { + this.menuTextPaddingRight = menuTextPaddingRight; + return this; + } + + public Builder menuTextPaddingRight(int menuTextPaddingRight) { + this.menuTextPaddingRight = (float) menuTextPaddingRight; + return this; + } + + public Builder menuTextPaddingRightRes(@DimenRes int menuTextPaddingRight) { + this.menuTextPaddingRight = Res.getDimension(menuTextPaddingRight); + return this; + } + + public Builder menuDropShadowColor(@ColorInt int color) { + this.menuDropShadowColor = color; + return this; + } + + public Builder menuDropShadowColorRes(@ColorRes int colorRes) { + this.menuDropShadowColor = Res.getColor(colorRes); + return this; + } + + public Builder menuDropShadowSize(float menuDropShadowSize) { + this.menuDropShadowSize = menuDropShadowSize; + return this; + } + + public Builder menuDropShadowSize(int menuDropShadowSize) { + this.menuDropShadowSize = (float) menuDropShadowSize; + return this; + } + + public Builder menuDropShadowSizeRes(@DimenRes int menuDropShadowSize) { + this.menuDropShadowSize = Res.getDimension(menuDropShadowSize); + return this; + } + + public Builder menuSelector(@DrawableRes int selectorRes) { + this.menuSelector = selectorRes; + return this; + } + + public Builder menuTextSize(float menuTextSize) { + this.menuTextSize = menuTextSize; + return this; + } + + public Builder menuTextSize(int menuTextSize) { + this.menuTextSize = (float) menuTextSize; + return this; + } + + public Builder menuTextSizeRes(@DimenRes int menuTextSize) { + this.menuTextSize = Res.getDimension(menuTextSize); + return this; + } + + public Builder menuTextFont(String menuTextFont) { + this.menuTextFont = menuTextFont; + return this; + } + + public Builder menuTextColor(@ColorInt int color) { + this.menuTextColor = color; + return this; + } + + public Builder menuTextColorRes(@ColorRes int colorRes) { + this.menuTextColor = Res.getColor(colorRes); + return this; + } + + public Builder showMenuRefresh(boolean showMenuRefresh) { + this.showMenuRefresh = showMenuRefresh; + return this; + } + + public Builder stringResRefresh(@StringRes int stringResRefresh) { + this.stringResRefresh = stringResRefresh; + return this; + } + + public Builder showMenuFind(boolean showMenuFind) { + this.showMenuFind = showMenuFind; + return this; + } + + public Builder stringResFind(@StringRes int stringResFind) { + this.stringResFind = stringResFind; + return this; + } + + public Builder showMenuShareVia(boolean showMenuShareVia) { + this.showMenuShareVia = showMenuShareVia; + return this; + } + + public Builder stringResShareVia(@StringRes int stringResShareVia) { + this.stringResShareVia = stringResShareVia; + return this; + } + + public Builder showMenuCopyLink(boolean showMenuCopyLink) { + this.showMenuCopyLink = showMenuCopyLink; + return this; + } + + public Builder stringResCopyLink(@StringRes int stringResCopyLink) { + this.stringResCopyLink = stringResCopyLink; + return this; + } + + public Builder showMenuOpenWith(boolean showMenuOpenWith) { + this.showMenuOpenWith = showMenuOpenWith; + return this; + } + + public Builder stringResOpenWith(@StringRes int stringResOpenWith) { + this.stringResOpenWith = stringResOpenWith; + return this; + } + + public Builder showMenuSavePhoto(boolean showMenuSavePhoto) { + this.showMenuSavePhoto = showMenuSavePhoto; + return this; + } + + public Builder stringResSavePhoto(@StringRes int stringResSavePhoto) { + this.stringResSavePhoto = stringResSavePhoto; + return this; + } + + public Builder showToastPhotoSavedOrFailed(boolean showToastPhotoSavedOrFailed) { + this.showToastPhotoSavedOrFailed = showToastPhotoSavedOrFailed; + return this; + } + + public Builder stringResPhotoSavedTo(@StringRes int stringResPhotoSavedTo) { + this.stringResPhotoSavedTo = stringResPhotoSavedTo; + return this; + } + + public Builder stringResPhotoSaveFailed(@StringRes int stringResPhotoSaveFailed) { + this.stringResPhotoSaveFailed = stringResPhotoSaveFailed; + return this; + } + + public Builder fileChooserEnabled(boolean fileChooserEnabled) { + this.fileChooserEnabled = fileChooserEnabled; + return this; + } + + public Builder stringResFileChooserTitle(@StringRes int stringResFileChooserTitle) { + this.stringResFileChooserTitle = stringResFileChooserTitle; + return this; + } + + public Builder customMenus(@NonNull List customMenus) { + this.customMenus = customMenus; + return this; + } + + public Builder addCustomMenu(@NonNull CustomMenu customMenu) { + if (customMenus == null) { + customMenus = new ArrayList<>(1); + } + customMenus.add(customMenu); + return this; + } + + public Builder setCustomAnimations(@AnimRes int animationOpenEnter, + @AnimRes int animationOpenExit, @AnimRes int animationCloseEnter, + @AnimRes int animationCloseExit) { + this.animationOpenEnter = animationOpenEnter; + this.animationOpenExit = animationOpenExit; + this.animationCloseEnter = animationCloseEnter; + this.animationCloseExit = animationCloseExit; + return this; + } + + /** + * @deprecated As of release 1.0.1, replaced by {@link #setCustomAnimations(int, int, int, int)} + */ + public Builder setCloseAnimations(@AnimRes int animationCloseEnter, + @AnimRes int animationCloseExit) { + this.animationCloseEnter = animationCloseEnter; + this.animationCloseExit = animationCloseExit; + return this; + } + + public Builder backPressToClose(boolean backPressToClose) { + this.backPressToClose = backPressToClose; + return this; + } + + public Builder stringResCopiedToClipboard(@StringRes int stringResCopiedToClipboard) { + this.stringResCopiedToClipboard = stringResCopiedToClipboard; + return this; + } + + public Builder webViewSupportZoom(boolean webViewSupportZoom) { + this.webViewSupportZoom = webViewSupportZoom; + return this; + } + + public Builder webViewMediaPlaybackRequiresUserGesture( + boolean webViewMediaPlaybackRequiresUserGesture) { + this.webViewMediaPlaybackRequiresUserGesture = webViewMediaPlaybackRequiresUserGesture; + return this; + } + + public Builder webViewBuiltInZoomControls(boolean webViewBuiltInZoomControls) { + this.webViewBuiltInZoomControls = webViewBuiltInZoomControls; + return this; + } + + public Builder webViewDisplayZoomControls(boolean webViewDisplayZoomControls) { + this.webViewDisplayZoomControls = webViewDisplayZoomControls; + return this; + } + + public Builder webViewAllowFileAccess(boolean webViewAllowFileAccess) { + this.webViewAllowFileAccess = webViewAllowFileAccess; + return this; + } + + public Builder webViewAllowContentAccess(boolean webViewAllowContentAccess) { + this.webViewAllowContentAccess = webViewAllowContentAccess; + return this; + } + + public Builder webViewLoadWithOverviewMode(boolean webViewLoadWithOverviewMode) { + this.webViewLoadWithOverviewMode = webViewLoadWithOverviewMode; + return this; + } + + public Builder webViewSaveFormData(boolean webViewSaveFormData) { + this.webViewSaveFormData = webViewSaveFormData; + return this; + } + + public Builder webViewTextZoom(int webViewTextZoom) { + this.webViewTextZoom = webViewTextZoom; + return this; + } + + public Builder webViewUseWideViewPort(boolean webViewUseWideViewPort) { + this.webViewUseWideViewPort = webViewUseWideViewPort; + return this; + } + + public Builder webViewSupportMultipleWindows(boolean webViewSupportMultipleWindows) { + this.webViewSupportMultipleWindows = webViewSupportMultipleWindows; + return this; + } + + public Builder webViewLayoutAlgorithm(WebSettings.LayoutAlgorithm webViewLayoutAlgorithm) { + this.webViewLayoutAlgorithm = webViewLayoutAlgorithm; + return this; + } + + public Builder webViewStandardFontFamily(String webViewStandardFontFamily) { + this.webViewStandardFontFamily = webViewStandardFontFamily; + return this; + } + + public Builder webViewFixedFontFamily(String webViewFixedFontFamily) { + this.webViewFixedFontFamily = webViewFixedFontFamily; + return this; + } + + public Builder webViewSansSerifFontFamily(String webViewSansSerifFontFamily) { + this.webViewSansSerifFontFamily = webViewSansSerifFontFamily; + return this; + } + + public Builder webViewSerifFontFamily(String webViewSerifFontFamily) { + this.webViewSerifFontFamily = webViewSerifFontFamily; + return this; + } + + public Builder webViewCursiveFontFamily(String webViewCursiveFontFamily) { + this.webViewCursiveFontFamily = webViewCursiveFontFamily; + return this; + } + + public Builder webViewFantasyFontFamily(String webViewFantasyFontFamily) { + this.webViewFantasyFontFamily = webViewFantasyFontFamily; + return this; + } + + public Builder webViewMinimumFontSize(int webViewMinimumFontSize) { + this.webViewMinimumFontSize = webViewMinimumFontSize; + return this; + } + + public Builder webViewMinimumLogicalFontSize(int webViewMinimumLogicalFontSize) { + this.webViewMinimumLogicalFontSize = webViewMinimumLogicalFontSize; + return this; + } + + public Builder webViewDefaultFontSize(int webViewDefaultFontSize) { + this.webViewDefaultFontSize = webViewDefaultFontSize; + return this; + } + + public Builder webViewDefaultFixedFontSize(int webViewDefaultFixedFontSize) { + this.webViewDefaultFixedFontSize = webViewDefaultFixedFontSize; + return this; + } + + public Builder webViewLoadsImagesAutomatically(boolean webViewLoadsImagesAutomatically) { + this.webViewLoadsImagesAutomatically = webViewLoadsImagesAutomatically; + return this; + } + + public Builder webViewBlockNetworkImage(boolean webViewBlockNetworkImage) { + this.webViewBlockNetworkImage = webViewBlockNetworkImage; + return this; + } + + public Builder webViewBlockNetworkLoads(boolean webViewBlockNetworkLoads) { + this.webViewBlockNetworkLoads = webViewBlockNetworkLoads; + return this; + } + + public Builder webViewJavaScriptEnabled(boolean webViewJavaScriptEnabled) { + this.webViewJavaScriptEnabled = webViewJavaScriptEnabled; + return this; + } + + public Builder webViewAllowUniversalAccessFromFileURLs( + boolean webViewAllowUniversalAccessFromFileURLs) { + this.webViewAllowUniversalAccessFromFileURLs = webViewAllowUniversalAccessFromFileURLs; + return this; + } + + public Builder webViewAllowFileAccessFromFileURLs(boolean webViewAllowFileAccessFromFileURLs) { + this.webViewAllowFileAccessFromFileURLs = webViewAllowFileAccessFromFileURLs; + return this; + } + + public Builder webViewGeolocationDatabasePath(String webViewGeolocationDatabasePath) { + this.webViewGeolocationDatabasePath = webViewGeolocationDatabasePath; + return this; + } + + public Builder webViewAppCacheEnabled(boolean webViewAppCacheEnabled) { + this.webViewAppCacheEnabled = webViewAppCacheEnabled; + return this; + } + + public Builder webViewDatabaseEnabled(boolean webViewDatabaseEnabled) { + this.webViewDatabaseEnabled = webViewDatabaseEnabled; + return this; + } + + public Builder webViewDomStorageEnabled(boolean webViewDomStorageEnabled) { + this.webViewDomStorageEnabled = webViewDomStorageEnabled; + return this; + } + + public Builder webViewGeolocationEnabled(boolean webViewGeolocationEnabled) { + this.webViewGeolocationEnabled = webViewGeolocationEnabled; + return this; + } + + public Builder webViewJavaScriptCanOpenWindowsAutomatically( + boolean webViewJavaScriptCanOpenWindowsAutomatically) { + this.webViewJavaScriptCanOpenWindowsAutomatically = + webViewJavaScriptCanOpenWindowsAutomatically; + return this; + } + + public Builder webViewDefaultTextEncodingName(String webViewDefaultTextEncodingName) { + this.webViewDefaultTextEncodingName = webViewDefaultTextEncodingName; + return this; + } + + public Builder webViewUserAgentString(String webViewUserAgentString) { + this.webViewUserAgentString = webViewUserAgentString; + return this; + } + + public Builder webViewUserAgentAppend(boolean webViewUserAgentAppend) { + this.webViewUserAgentAppend = webViewUserAgentAppend; + return this; + } + + public Builder webViewNeedInitialFocus(boolean webViewNeedInitialFocus) { + this.webViewNeedInitialFocus = webViewNeedInitialFocus; + return this; + } + + public Builder webViewCacheMode(int webViewCacheMode) { + this.webViewCacheMode = webViewCacheMode; + return this; + } + + public Builder webViewMixedContentMode(int webViewMixedContentMode) { + this.webViewMixedContentMode = webViewMixedContentMode; + return this; + } + + public Builder webViewOffscreenPreRaster(boolean webViewOffscreenPreRaster) { + this.webViewOffscreenPreRaster = webViewOffscreenPreRaster; + return this; + } + + public Builder webViewAppJumpEnabled(boolean webViewAppJumpEnabled) { + this.webViewAppJumpEnabled = webViewAppJumpEnabled; + return this; + } + + public Builder webViewCookieEnabled(boolean webViewCookieEnabled) { + this.webViewCookieEnabled = webViewCookieEnabled; + return this; + } + + public Builder webViewCameraEnabled(boolean webViewCameraEnabled) { + this.webViewCameraEnabled = webViewCameraEnabled; + return this; + } + + public Builder webViewAudioEnabled(boolean webViewAudioEnabled) { + this.webViewAudioEnabled = webViewAudioEnabled; + return this; + } + + public Builder setHeader(Map extraHeaders) { + this.extraHeaders = extraHeaders; + return this; + } + + public Builder headersMainPage(boolean extraHeadersMainPage) { + this.extraHeadersMainPage = extraHeadersMainPage; + return this; + } + + /** + * @deprecated As of release 1.1.1, replaced by {@link #webViewUserAgentString(String)} + * Use setUserAgentString("Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.4) Gecko/20100101 + * Firefox/4.0") instead + */ + public Builder webViewDesktopMode(boolean webViewDesktopMode) { + return webViewUserAgentString( + "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.4) Gecko/20100101 Firefox/4.0"); + } + + public Builder injectJavaScript(String injectJavaScript) { + this.injectJavaScript = injectJavaScript; + return this; + } + + public Builder injectJavaScriptMainPage(boolean injectJavaScriptMainPage) { + this.injectJavaScriptMainPage = injectJavaScriptMainPage; + return this; + } + + public Builder injectCookies(Map> injectCookies) { + this.injectCookies = injectCookies; + return this; + } + + public void load(@StringRes int dataRes) { + load(Res.getString(dataRes)); + } + + public void load(String data) { + load(data, "text/html", "UTF-8"); + } + + public void load(String data, String mimeType, String encoding) { + this.mimeType = mimeType; + this.encoding = encoding; + show(null, data); + } + + public void show(@StringRes int urlRes) { + show(Res.getString(urlRes)); + } + + public void show(@NonNull String url) { + show(url, null); + } + + protected void show(String url, String data) { + this.url = url; + this.data = data; + this.key = System.identityHashCode(this); + + if (!listeners.isEmpty()) new BroadCastManager(context, key, listeners); + + Intent intent = new Intent(context, AwesomeWebViewActivity.class); + intent.putExtra("builder", this); + + Ctx.startActivity(intent); + + if (context instanceof Activity) { + ((Activity) context).overridePendingTransition(animationOpenEnter, animationOpenExit); + } + } + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/AwesomeWebViewActivity.java b/library/src/main/java/com/wuadam/awesomewebview/AwesomeWebViewActivity.java new file mode 100644 index 0000000..693a21f --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/AwesomeWebViewActivity.java @@ -0,0 +1,1748 @@ +package com.wuadam.awesomewebview; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.PorterDuff; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.StateListDrawable; +import android.net.MailTo; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.provider.MediaStore; +import androidx.annotation.DrawableRes; +import androidx.annotation.RequiresApi; +import com.google.android.material.appbar.AppBarLayout; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import com.google.android.material.snackbar.Snackbar; +import androidx.core.content.ContextCompat; +import androidx.core.content.FileProvider; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.AppCompatImageButton; +import androidx.appcompat.widget.Toolbar; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.webkit.CookieManager; +import android.webkit.CookieSyncManager; +import android.webkit.DownloadListener; +import android.webkit.GeolocationPermissions; +import android.webkit.PermissionRequest; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.thefinestartist.converters.UnitConverter; +import com.thefinestartist.utils.etc.APILevel; +import com.thefinestartist.utils.service.ClipboardManagerUtil; +import com.thefinestartist.utils.ui.DisplayUtil; +import com.thefinestartist.utils.ui.ViewUtil; +import com.wuadam.awesomewebview.enums.Position; +import com.wuadam.awesomewebview.helpers.BitmapHelper; +import com.wuadam.awesomewebview.helpers.ColorHelper; +import com.wuadam.awesomewebview.helpers.DownPicUtil; +import com.wuadam.awesomewebview.helpers.PermissionHelper; +import com.wuadam.awesomewebview.helpers.TypefaceHelper; +import com.wuadam.awesomewebview.helpers.UrlParser; +import com.wuadam.awesomewebview.jsInterface.CommonJsHelper; +import com.wuadam.awesomewebview.listeners.BroadCastManager; +import com.wuadam.awesomewebview.objects.CustomMenu; +import com.wuadam.awesomewebview.views.ShadowLayout; +import com.wuadam.awesomewebview.views.VideoEnabledWebChromeClient; +import com.wuadam.awesomewebview.views.VideoEnabledWebView; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +//MailTo Imports + +/** + * Created by Leonardo on 11/14/15. + */ +public class AwesomeWebViewActivity extends AppCompatActivity + implements View.OnClickListener, Handler.Callback { + + protected int key; + + protected boolean rtl; + protected int theme; + + protected int statusBarColor; + protected boolean statusBarIconDark; + + protected int toolbarColor; + protected boolean toolbarVisible; + + protected int iconDefaultColor; + protected int iconDisabledColor; + protected int iconPressedColor; + protected int iconSelector; + + protected boolean showIconClose; + protected boolean disableIconClose; + protected boolean showIconBack; + protected boolean disableIconBack; + protected boolean showIconForward; + protected boolean disableIconForward; + protected boolean showIconMenu; + protected boolean disableIconMenu; + + protected boolean showDivider; + protected boolean gradientDivider; + protected int dividerColor; + protected float dividerHeight; + + protected boolean showProgressBar; + protected int progressBarColor; + protected float progressBarHeight; + protected Position progressBarPosition; + + protected String titleDefault; + protected boolean updateTitleFromHtml; + protected float titleSize; + protected String titleFont; + protected int titleColor; + + protected boolean showUrl; + protected float urlSize; + protected String urlFont; + protected int urlColor; + + protected int menuColor; + protected int menuDropShadowColor; + protected float menuDropShadowSize; + protected int menuSelector; + + protected float menuTextSize; + protected String menuTextFont; + protected int menuTextColor; + + protected int menuTextGravity; + protected float menuTextPaddingLeft; + protected float menuTextPaddingRight; + + protected boolean showMenuRefresh; + protected int stringResRefresh; + protected boolean showMenuFind; + protected int stringResFind; + protected boolean showMenuShareVia; + protected int stringResShareVia; + protected boolean showMenuCopyLink; + protected int stringResCopyLink; + protected boolean showMenuOpenWith; + protected int stringResOpenWith; + protected boolean showMenuSavePhoto; + protected int stringResSavePhoto; + protected boolean showToastPhotoSavedOrFailed; + protected int stringResPhotoSavedTo; + protected int stringResPhotoSaveFailed; + protected boolean fileChooserEnabled; + protected int stringResFileChooserTitle; + + protected List customMenus; + + protected int animationCloseEnter; + protected int animationCloseExit; + + protected boolean backPressToClose; + protected int stringResCopiedToClipboard; + + protected Boolean webViewSupportZoom; + protected Boolean webViewMediaPlaybackRequiresUserGesture; + protected Boolean webViewBuiltInZoomControls; + protected Boolean webViewDisplayZoomControls; + protected Boolean webViewAllowFileAccess; + protected Boolean webViewAllowContentAccess; + protected Boolean webViewLoadWithOverviewMode; + protected Boolean webViewSaveFormData; + protected Integer webViewTextZoom; + protected Boolean webViewUseWideViewPort; + protected Boolean webViewSupportMultipleWindows; + protected WebSettings.LayoutAlgorithm webViewLayoutAlgorithm; + protected String webViewStandardFontFamily; + protected String webViewFixedFontFamily; + protected String webViewSansSerifFontFamily; + protected String webViewSerifFontFamily; + protected String webViewCursiveFontFamily; + protected String webViewFantasyFontFamily; + protected Integer webViewMinimumFontSize; + protected Integer webViewMinimumLogicalFontSize; + protected Integer webViewDefaultFontSize; + protected Integer webViewDefaultFixedFontSize; + protected Boolean webViewLoadsImagesAutomatically; + protected Boolean webViewBlockNetworkImage; + protected Boolean webViewBlockNetworkLoads; + protected Boolean webViewJavaScriptEnabled; + protected Boolean webViewAllowUniversalAccessFromFileURLs; + protected Boolean webViewAllowFileAccessFromFileURLs; + protected String webViewGeolocationDatabasePath; + protected Boolean webViewAppCacheEnabled; + protected Boolean webViewDatabaseEnabled; + protected Boolean webViewDomStorageEnabled; + protected Boolean webViewGeolocationEnabled; + protected Boolean webViewJavaScriptCanOpenWindowsAutomatically; + protected String webViewDefaultTextEncodingName; + protected String webViewUserAgentString; + protected Boolean webViewUserAgentAppend; + protected Boolean webViewNeedInitialFocus; + protected Integer webViewCacheMode; + protected Integer webViewMixedContentMode; + protected Boolean webViewOffscreenPreRaster; + protected Boolean webViewAppJumpEnabled; + protected Boolean webViewCookieEnabled; + protected Boolean webViewCameraEnabled; + protected Boolean webViewAudioEnabled; + + protected String filePickerCamMessage; + protected ValueCallback filePickerFileMessage; + protected ValueCallback filePickerFilePath; + protected final static int FILE_PICKER_REQ_CODE = 1; + protected String FILE_TYPE = "*/*"; + + protected String injectJavaScript; + protected Boolean injectJavaScriptMainPage; + + protected Map> injectCookies; + + protected String mimeType; + protected String encoding; + protected String data; + protected String url; + protected Map extraHeaders; + protected Boolean extraHeadersMainPage; + protected CoordinatorLayout coordinatorLayout; + protected AppBarLayout appBar; + protected Toolbar toolbar; + protected RelativeLayout toolbarLayout; + protected TextView title; + protected TextView urlTv; + protected AppCompatImageButton close; + protected AppCompatImageButton back; + protected AppCompatImageButton forward; + protected AppCompatImageButton more; + protected WebView webView; + protected WebChromeClient webChromeClient; + protected WebViewClient webViewClient; + protected View gradient; + protected View divider; + protected ProgressBar progressBar; + protected RelativeLayout menuLayout; + protected ShadowLayout shadowLayout; + protected LinearLayout menuBackground; + protected LinearLayout menuRefresh; + protected TextView menuRefreshTv; + protected LinearLayout menuFind; + protected TextView menuFindTv; + protected LinearLayout menuShareVia; + protected TextView menuShareViaTv; + protected LinearLayout menuCopyLink; + protected TextView menuCopyLinkTv; + protected LinearLayout menuOpenWith; + protected TextView menuOpenWithTv; + protected FrameLayout webLayout; + DownloadListener downloadListener = new DownloadListener() { + @Override + public void onDownloadStart(String url, String userAgent, String contentDisposition, + String mimetype, long contentLength) { + BroadCastManager.onDownloadStart(AwesomeWebViewActivity.this, key, url, userAgent, + contentDisposition, mimetype, contentLength); + } + }; + + protected Handler handler = new Handler(this); + protected final int MSG_CLICK_ON_WEBVIEW = 1; + protected final int MSG_CLICK_ON_URL = 2; + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + protected void initializeOptions() { + Intent intent = getIntent(); + if (intent == null) return; + + AwesomeWebView.Builder builder = (AwesomeWebView.Builder) intent.getSerializableExtra("builder"); + + // set theme before resolving attributes depending on those + setTheme(builder.theme != null ? builder.theme : 0); + + // resolve themed attributes + TypedValue typedValue = new TypedValue(); + TypedArray typedArray = obtainStyledAttributes(typedValue.data, new int[]{ + android.R.attr.colorPrimaryDark, android.R.attr.colorPrimary, android.R.attr.colorAccent, + android.R.attr.textColorPrimary, android.R.attr.textColorSecondary, + android.R.attr.selectableItemBackground, android.R.attr.selectableItemBackgroundBorderless + }); + int colorPrimaryDark = typedArray.getColor(0, ContextCompat.getColor(this, R.color.finestGray)); + int colorPrimary = typedArray.getColor(1, ContextCompat.getColor(this, R.color.finestWhite)); + int colorAccent = typedArray.getColor(2, ContextCompat.getColor(this, R.color.finestBlack)); + int textColorPrimary = + typedArray.getColor(3, ContextCompat.getColor(this, R.color.finestBlack)); + int textColorSecondary = + typedArray.getColor(4, ContextCompat.getColor(this, R.color.finestSilver)); + int selectableItemBackground = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? typedArray.getResourceId(5, 0) + : R.drawable.selector_light_theme; + int selectableItemBackgroundBorderless = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? typedArray.getResourceId(6, 0) + : R.drawable.selector_light_theme; + typedArray.recycle(); + + key = builder.key; + + rtl = builder.rtl != null ? builder.rtl : getResources().getBoolean(R.bool.is_right_to_left); + + statusBarColor = builder.statusBarColor != null ? builder.statusBarColor : colorPrimaryDark; + statusBarIconDark = builder.statusBarIconDark != null ? builder.statusBarIconDark : false; + + toolbarColor = builder.toolbarColor != null ? builder.toolbarColor : colorPrimary; + toolbarVisible = builder.toolbarVisible != null ? builder.toolbarVisible : true; + + iconDefaultColor = builder.iconDefaultColor != null ? builder.iconDefaultColor : colorAccent; + iconDisabledColor = builder.iconDisabledColor != null ? builder.iconDisabledColor + : ColorHelper.disableColor(iconDefaultColor); + iconPressedColor = + builder.iconPressedColor != null ? builder.iconPressedColor : iconDefaultColor; + iconSelector = + builder.iconSelector != null ? builder.iconSelector : selectableItemBackgroundBorderless; + + showIconClose = builder.showIconClose != null ? builder.showIconClose : true; + disableIconClose = builder.disableIconClose != null ? builder.disableIconClose : false; + showIconBack = builder.showIconBack != null ? builder.showIconBack : true; + disableIconBack = builder.disableIconBack != null ? builder.disableIconBack : false; + showIconForward = builder.showIconForward != null ? builder.showIconForward : true; + disableIconForward = builder.disableIconForward != null ? builder.disableIconForward : false; + showIconMenu = builder.showIconMenu != null ? builder.showIconMenu : true; + disableIconMenu = builder.disableIconMenu != null ? builder.disableIconMenu : false; + + showDivider = builder.showDivider != null ? builder.showDivider : true; + gradientDivider = builder.gradientDivider != null ? builder.gradientDivider : true; + dividerColor = builder.dividerColor != null ? builder.dividerColor + : ContextCompat.getColor(this, R.color.finestBlack10); + dividerHeight = builder.dividerHeight != null ? builder.dividerHeight + : getResources().getDimension(R.dimen.defaultDividerHeight); + + showProgressBar = builder.showProgressBar != null ? builder.showProgressBar : true; + progressBarColor = builder.progressBarColor != null ? builder.progressBarColor : colorAccent; + progressBarHeight = builder.progressBarHeight != null ? builder.progressBarHeight + : getResources().getDimension(R.dimen.defaultProgressBarHeight); + progressBarPosition = builder.progressBarPosition != null ? builder.progressBarPosition + : Position.BOTTOM_OF_TOOLBAR; + + titleDefault = builder.titleDefault; + updateTitleFromHtml = builder.updateTitleFromHtml != null ? builder.updateTitleFromHtml : true; + titleSize = builder.titleSize != null ? builder.titleSize + : getResources().getDimension(R.dimen.defaultTitleSize); + titleFont = builder.titleFont != null ? builder.titleFont : "Roboto-Medium.ttf"; + titleColor = builder.titleColor != null ? builder.titleColor : textColorPrimary; + + showUrl = builder.showUrl != null ? builder.showUrl : true; + urlSize = builder.urlSize != null ? builder.urlSize + : getResources().getDimension(R.dimen.defaultUrlSize); + urlFont = builder.urlFont != null ? builder.urlFont : "Roboto-Regular.ttf"; + urlColor = builder.urlColor != null ? builder.urlColor : textColorSecondary; + + menuColor = builder.menuColor != null ? builder.menuColor + : ContextCompat.getColor(this, R.color.finestWhite); + menuDropShadowColor = builder.menuDropShadowColor != null ? builder.menuDropShadowColor + : ContextCompat.getColor(this, R.color.finestBlack10); + menuDropShadowSize = builder.menuDropShadowSize != null ? builder.menuDropShadowSize + : getResources().getDimension(R.dimen.defaultMenuDropShadowSize); + menuSelector = builder.menuSelector != null ? builder.menuSelector : selectableItemBackground; + + menuTextSize = builder.menuTextSize != null ? builder.menuTextSize + : getResources().getDimension(R.dimen.defaultMenuTextSize); + menuTextFont = builder.menuTextFont != null ? builder.menuTextFont : "Roboto-Regular.ttf"; + menuTextColor = builder.menuTextColor != null ? builder.menuTextColor + : ContextCompat.getColor(this, R.color.finestBlack); + + menuTextGravity = builder.menuTextGravity != null ? builder.menuTextGravity + : Gravity.CENTER_VERTICAL | Gravity.START; + menuTextPaddingLeft = builder.menuTextPaddingLeft != null ? builder.menuTextPaddingLeft + : rtl ? getResources().getDimension(R.dimen.defaultMenuTextPaddingRight) + : getResources().getDimension(R.dimen.defaultMenuTextPaddingLeft); + menuTextPaddingRight = builder.menuTextPaddingRight != null ? builder.menuTextPaddingRight + : rtl ? getResources().getDimension(R.dimen.defaultMenuTextPaddingLeft) + : getResources().getDimension(R.dimen.defaultMenuTextPaddingRight); + + showMenuRefresh = builder.showMenuRefresh != null ? builder.showMenuRefresh : true; + stringResRefresh = + builder.stringResRefresh != null ? builder.stringResRefresh : R.string.refresh; + showMenuFind = builder.showMenuFind != null ? builder.showMenuFind : false; + stringResFind = builder.stringResFind != null ? builder.stringResFind : R.string.find; + showMenuShareVia = builder.showMenuShareVia != null ? builder.showMenuShareVia : true; + stringResShareVia = + builder.stringResShareVia != null ? builder.stringResShareVia : R.string.share_via; + showMenuCopyLink = builder.showMenuCopyLink != null ? builder.showMenuCopyLink : true; + stringResCopyLink = + builder.stringResCopyLink != null ? builder.stringResCopyLink : R.string.copy_link; + showMenuOpenWith = builder.showMenuOpenWith != null ? builder.showMenuOpenWith : true; + stringResOpenWith = + builder.stringResOpenWith != null ? builder.stringResOpenWith : R.string.open_with; + showMenuSavePhoto = builder.showMenuSavePhoto != null ? builder.showMenuSavePhoto : true; + stringResSavePhoto = + builder.stringResSavePhoto != null ? builder.stringResSavePhoto : R.string.save_photo; + showToastPhotoSavedOrFailed = builder.showToastPhotoSavedOrFailed != null ? builder.showToastPhotoSavedOrFailed : true; + stringResPhotoSavedTo = + builder.stringResPhotoSavedTo != null ? builder.stringResPhotoSavedTo : R.string.photo_saved_to; + stringResPhotoSaveFailed = + builder.stringResPhotoSaveFailed != null ? builder.stringResPhotoSaveFailed : R.string.photo_save_failed; + fileChooserEnabled = builder.fileChooserEnabled != null ? builder.fileChooserEnabled : true; + stringResFileChooserTitle = + builder.stringResFileChooserTitle != null ? builder.stringResFileChooserTitle : R.string.file_chooser; + + customMenus = builder.customMenus != null? builder.customMenus: new ArrayList(0); + + animationCloseEnter = builder.animationCloseEnter != null ? builder.animationCloseEnter + : R.anim.modal_activity_close_enter; + animationCloseExit = builder.animationCloseExit != null ? builder.animationCloseExit + : R.anim.modal_activity_close_exit; + + backPressToClose = builder.backPressToClose != null ? builder.backPressToClose : false; + stringResCopiedToClipboard = + builder.stringResCopiedToClipboard != null ? builder.stringResCopiedToClipboard + : R.string.copied_to_clipboard; + + webViewSupportZoom = builder.webViewSupportZoom; + webViewMediaPlaybackRequiresUserGesture = builder.webViewMediaPlaybackRequiresUserGesture; + webViewBuiltInZoomControls = + builder.webViewBuiltInZoomControls != null ? builder.webViewBuiltInZoomControls : false; + webViewDisplayZoomControls = + builder.webViewDisplayZoomControls != null ? builder.webViewDisplayZoomControls : false; + webViewAllowFileAccess = + builder.webViewAllowFileAccess != null ? builder.webViewAllowFileAccess : true; + webViewAllowContentAccess = builder.webViewAllowContentAccess; + webViewLoadWithOverviewMode = + builder.webViewLoadWithOverviewMode != null ? builder.webViewLoadWithOverviewMode : true; + webViewSaveFormData = builder.webViewSaveFormData; + webViewTextZoom = builder.webViewTextZoom; + webViewUseWideViewPort = builder.webViewUseWideViewPort; + webViewSupportMultipleWindows = builder.webViewSupportMultipleWindows; + webViewLayoutAlgorithm = builder.webViewLayoutAlgorithm; + webViewStandardFontFamily = builder.webViewStandardFontFamily; + webViewFixedFontFamily = builder.webViewFixedFontFamily; + webViewSansSerifFontFamily = builder.webViewSansSerifFontFamily; + webViewSerifFontFamily = builder.webViewSerifFontFamily; + webViewCursiveFontFamily = builder.webViewCursiveFontFamily; + webViewFantasyFontFamily = builder.webViewFantasyFontFamily; + webViewMinimumFontSize = builder.webViewMinimumFontSize; + webViewMinimumLogicalFontSize = builder.webViewMinimumLogicalFontSize; + webViewDefaultFontSize = builder.webViewDefaultFontSize; + webViewDefaultFixedFontSize = builder.webViewDefaultFixedFontSize; + webViewLoadsImagesAutomatically = builder.webViewLoadsImagesAutomatically; + webViewBlockNetworkImage = builder.webViewBlockNetworkImage; + webViewBlockNetworkLoads = builder.webViewBlockNetworkLoads; + webViewJavaScriptEnabled = + builder.webViewJavaScriptEnabled != null ? builder.webViewJavaScriptEnabled : true; + webViewAllowUniversalAccessFromFileURLs = builder.webViewAllowUniversalAccessFromFileURLs; + webViewAllowFileAccessFromFileURLs = builder.webViewAllowFileAccessFromFileURLs; + webViewGeolocationDatabasePath = builder.webViewGeolocationDatabasePath; + webViewAppCacheEnabled = + builder.webViewAppCacheEnabled != null ? builder.webViewAppCacheEnabled : true; + webViewDatabaseEnabled = builder.webViewDatabaseEnabled; + webViewDomStorageEnabled = + builder.webViewDomStorageEnabled != null ? builder.webViewDomStorageEnabled : true; + webViewGeolocationEnabled = builder.webViewGeolocationEnabled; + webViewJavaScriptCanOpenWindowsAutomatically = + builder.webViewJavaScriptCanOpenWindowsAutomatically; + webViewDefaultTextEncodingName = builder.webViewDefaultTextEncodingName; + webViewUserAgentString = builder.webViewUserAgentString; + webViewUserAgentAppend = builder.webViewUserAgentAppend; + webViewNeedInitialFocus = builder.webViewNeedInitialFocus; + webViewCacheMode = builder.webViewCacheMode; + webViewMixedContentMode = builder.webViewMixedContentMode; + webViewOffscreenPreRaster = builder.webViewOffscreenPreRaster; + webViewAppJumpEnabled = builder.webViewAppJumpEnabled != null ? builder.webViewAppJumpEnabled : true; + webViewCookieEnabled = builder.webViewCookieEnabled != null ? builder.webViewCookieEnabled : true; + webViewCameraEnabled = builder.webViewCameraEnabled != null ? builder.webViewCameraEnabled : true; + webViewAudioEnabled = builder.webViewAudioEnabled != null ? builder.webViewAudioEnabled : true; + + injectJavaScript = builder.injectJavaScript; + injectJavaScriptMainPage = builder.injectJavaScriptMainPage != null ? builder.injectJavaScriptMainPage : true; + extraHeadersMainPage = builder.extraHeadersMainPage != null ? builder.extraHeadersMainPage : true; + injectCookies = builder.injectCookies; + + mimeType = builder.mimeType; + encoding = builder.encoding; + data = builder.data; + url = builder.url; + extraHeaders = builder.extraHeaders; + } + + protected void bindViews() { + coordinatorLayout = findViewById(R.id.coordinatorLayout); + + appBar = findViewById(R.id.appBar); + toolbar = findViewById(R.id.toolbar); + toolbarLayout = findViewById(R.id.toolbarLayout); + + title = findViewById(R.id.title); + urlTv = findViewById(R.id.url); + + close = findViewById(R.id.close); + back = findViewById(R.id.back); + forward = findViewById(R.id.forward); + more = findViewById(R.id.more); + + close.setOnClickListener(this); + back.setOnClickListener(this); + forward.setOnClickListener(this); + more.setOnClickListener(this); + + gradient = findViewById(R.id.gradient); + divider = findViewById(R.id.divider); + progressBar = findViewById(R.id.progressBar); + + menuLayout = findViewById(R.id.menuLayout); + shadowLayout = findViewById(R.id.shadowLayout); + menuBackground = findViewById(R.id.menuBackground); + + menuRefresh = findViewById(R.id.menuRefresh); + menuRefreshTv = findViewById(R.id.menuRefreshTv); + menuFind = findViewById(R.id.menuFind); + menuFindTv = findViewById(R.id.menuFindTv); + menuShareVia = findViewById(R.id.menuShareVia); + menuShareViaTv = findViewById(R.id.menuShareViaTv); + menuCopyLink = findViewById(R.id.menuCopyLink); + menuCopyLinkTv = findViewById(R.id.menuCopyLinkTv); + menuOpenWith = findViewById(R.id.menuOpenWith); + menuOpenWithTv = findViewById(R.id.menuOpenWithTv); + + webLayout = findViewById(R.id.webLayout); + webView = buildWebView(); + webLayout.addView(webView); + } + + protected void layoutViews() { + if (!toolbarVisible) { + setSupportActionBar(toolbar); + toolbar.setVisibility(View.GONE); + } + + { // AppBar + float toolbarHeight = toolbarVisible? getResources().getDimension(R.dimen.toolbarHeight): 0; + if (!gradientDivider) toolbarHeight += dividerHeight; + CoordinatorLayout.LayoutParams params = + new CoordinatorLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + (int) toolbarHeight); + appBar.setLayoutParams(params); + coordinatorLayout.requestLayout(); + } + + { // Toolbar + float toolbarHeight = toolbarVisible? getResources().getDimension(R.dimen.toolbarHeight): 0; + LinearLayout.LayoutParams params = + new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) toolbarHeight); + toolbarLayout.setMinimumHeight((int) toolbarHeight); + toolbarLayout.setLayoutParams(params); + coordinatorLayout.requestLayout(); + } + + { // TextViews + int maxWidth = getMaxWidth(); + title.setMaxWidth(maxWidth); + urlTv.setMaxWidth(maxWidth); + requestCenterLayout(); + } + + { // Icons + updateIcon(close, rtl ? R.drawable.more : R.drawable.close); + updateIcon(back, R.drawable.back); + updateIcon(forward, R.drawable.forward); + updateIcon(more, rtl ? R.drawable.close : R.drawable.more); + } + + { // Divider + if (gradientDivider) { + float toolbarHeight = toolbarVisible? getResources().getDimension(R.dimen.toolbarHeight): 0; + CoordinatorLayout.LayoutParams params = + (CoordinatorLayout.LayoutParams) gradient.getLayoutParams(); + params.setMargins(0, (int) toolbarHeight, 0, 0); + gradient.setLayoutParams(params); + } + } + + { // ProgressBar + progressBar.setMinimumHeight((int) progressBarHeight); + CoordinatorLayout.LayoutParams params = + new CoordinatorLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + (int) progressBarHeight); + float toolbarHeight = toolbarVisible? getResources().getDimension(R.dimen.toolbarHeight): 0; + switch (progressBarPosition) { + case TOP_OF_TOOLBAR: + params.setMargins(0, 0, 0, 0); + break; + case BOTTOM_OF_TOOLBAR: + params.setMargins(0, (int) toolbarHeight - (int) progressBarHeight, 0, 0); + break; + case TOP_OF_WEBVIEW: + params.setMargins(0, (int) toolbarHeight, 0, 0); + break; + case BOTTOM_OF_WEBVIEW: + params.setMargins(0, DisplayUtil.getHeight() - (int) progressBarHeight, 0, 0); + break; + } + progressBar.setLayoutParams(params); + } + + { // WebLayout + float toolbarHeight = toolbarVisible? getResources().getDimension(R.dimen.toolbarHeight): 0; + int statusBarHeight = toolbarVisible? DisplayUtil.getStatusBarHeight(): 0; + int screenHeight = DisplayUtil.getHeight(); + float webLayoutMinimumHeight = screenHeight - toolbarHeight - statusBarHeight; + if (showDivider && !gradientDivider) webLayoutMinimumHeight -= dividerHeight; + webLayout.setMinimumHeight((int) webLayoutMinimumHeight); + + CoordinatorLayout.LayoutParams params = + new CoordinatorLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + params.setMargins(0, (int) toolbarHeight, 0, 0); + webLayout.setLayoutParams(params); + } + } + + @SuppressLint("SetJavaScriptEnabled") + protected void initializeViews() { + if (! toolbarVisible) { + setSupportActionBar(toolbar); + toolbar.setVisibility(View.GONE); + } + + { // StatusBar + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window window = getWindow(); + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(statusBarColor); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Window window = getWindow(); + if (statusBarIconDark) { + window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } else { + window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + } + } + } + + { // Toolbar + toolbar.setBackgroundColor(toolbarColor); + } + + { // TextViews + title.setText(titleDefault); + title.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize); + title.setTypeface(TypefaceHelper.get(this, titleFont)); + title.setTextColor(titleColor); + + urlTv.setVisibility(showUrl ? View.VISIBLE : View.GONE); + urlTv.setText(UrlParser.getHost(url)); + urlTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, urlSize); + urlTv.setTypeface(TypefaceHelper.get(this, urlFont)); + urlTv.setTextColor(urlColor); + + requestCenterLayout(); + } + + { // Icons + close.setBackgroundResource(iconSelector); + back.setBackgroundResource(iconSelector); + forward.setBackgroundResource(iconSelector); + more.setBackgroundResource(iconSelector); + + close.setVisibility(showIconClose ? View.VISIBLE : View.GONE); + close.setEnabled(!disableIconClose); + + if ((showMenuRefresh + || showMenuFind + || showMenuShareVia + || showMenuCopyLink + || showMenuOpenWith + || customMenus.size() > 0) && showIconMenu) { + more.setVisibility(View.VISIBLE); + } else { + more.setVisibility(View.GONE); + } + more.setEnabled(!disableIconMenu); + } + + { // Cookie + if (webViewCookieEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true); + } + } + + { // WebView + webChromeClient = buildWebChromeClient(); + webViewClient = buildWebViewClient(); + + webView.setWebChromeClient(webChromeClient); + webView.setWebViewClient(webViewClient); + webView.setDownloadListener(downloadListener); + + webView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + final WebView.HitTestResult hitTestResult = webView.getHitTestResult(); + // 如果是图片类型或者是带有图片链接的类型 + if (hitTestResult.getType() == WebView.HitTestResult.IMAGE_TYPE || + hitTestResult.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { + if (!showMenuSavePhoto) { + return false; + } + // 弹出保存图片的对话框 + AlertDialog.Builder builder = new AlertDialog.Builder(AwesomeWebViewActivity.this); + final String items[] = {getResources().getString(stringResSavePhoto)}; + builder.setItems(items, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + PermissionHelper.CheckPermissions(AwesomeWebViewActivity.this, new PermissionHelper.CheckPermissionListener() { + @Override + public void onAllGranted(boolean sync) { + String url = hitTestResult.getExtra(); + // 下载图片到本地 + CookieSyncManager.createInstance(AwesomeWebViewActivity.this); + CookieSyncManager.getInstance().sync(); + CookieManager cookieManager = CookieManager.getInstance(); + String cookie = cookieManager.getCookie(webView.getUrl()); + DownPicUtil.downPic(url, webView.getSettings().getUserAgentString(), webView.getUrl(), cookie, new DownPicUtil.DownFinishListener() { + + @Override + public void onDownFinish(String path) { + if (showToastPhotoSavedOrFailed) { + Toast.makeText(AwesomeWebViewActivity.this, getResources().getString(stringResPhotoSavedTo) + path, Toast.LENGTH_LONG).show(); + } + // 最后通知图库更新 + getApplicationContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + path))); + } + + @Override + public void onError() { + if (showToastPhotoSavedOrFailed) { + Toast.makeText(AwesomeWebViewActivity.this, getResources().getString(stringResPhotoSaveFailed), Toast.LENGTH_LONG).show(); + } + } + }); + } + + @Override + public void onPartlyGranted(List permissionsDenied, boolean sync) { + + } + }, Manifest.permission.WRITE_EXTERNAL_STORAGE); + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + return true; + } + return false; + } + }); + + webView.setOnTouchListener(new View.OnTouchListener() { + private float xDown, yDown; + private long timeDown; + @Override + public boolean onTouch(View v, MotionEvent event) { + if (v == webView && event.getAction() == MotionEvent.ACTION_DOWN) { + xDown = event.getX(); + yDown = event.getY(); + timeDown = System.currentTimeMillis(); + } else if (v == webView && event.getAction() == MotionEvent.ACTION_UP){ + if (Math.abs(xDown - event.getX()) < 50 && Math.abs(yDown - event.getY()) < 50 && System.currentTimeMillis() - timeDown < 200) { + // https://stackoverflow.com/a/5125620 + handler.sendEmptyMessageDelayed(MSG_CLICK_ON_WEBVIEW, 500); + } + } + return false; + } + }); + + WebSettings settings = webView.getSettings(); + + if (webViewSupportZoom != null) settings.setSupportZoom(webViewSupportZoom); + if (webViewMediaPlaybackRequiresUserGesture != null + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + settings.setMediaPlaybackRequiresUserGesture(webViewMediaPlaybackRequiresUserGesture); + } + if (webViewBuiltInZoomControls != null) { + settings.setBuiltInZoomControls(webViewBuiltInZoomControls); + + if (webViewBuiltInZoomControls) { + // Remove NestedScrollView to enable BuiltInZoomControls + ((ViewGroup) webView.getParent()).removeAllViews(); + } + } + if (webViewDisplayZoomControls != null + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + settings.setDisplayZoomControls(webViewDisplayZoomControls); + } + + if (webViewAllowFileAccess != null) settings.setAllowFileAccess(webViewAllowFileAccess); + if (webViewAllowContentAccess != null + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + settings.setAllowContentAccess(webViewAllowContentAccess); + } + if (webViewLoadWithOverviewMode != null) { + settings.setLoadWithOverviewMode(webViewLoadWithOverviewMode); + } + if (webViewSaveFormData != null) settings.setSaveFormData(webViewSaveFormData); + if (webViewTextZoom != null + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + settings.setTextZoom(webViewTextZoom); + } + if (webViewUseWideViewPort != null) settings.setUseWideViewPort(webViewUseWideViewPort); + if (webViewSupportMultipleWindows != null) { + settings.setSupportMultipleWindows(webViewSupportMultipleWindows); + } + if (webViewLayoutAlgorithm != null) settings.setLayoutAlgorithm(webViewLayoutAlgorithm); + if (webViewStandardFontFamily != null) { + settings.setStandardFontFamily(webViewStandardFontFamily); + } + if (webViewFixedFontFamily != null) settings.setFixedFontFamily(webViewFixedFontFamily); + if (webViewSansSerifFontFamily != null) { + settings.setSansSerifFontFamily(webViewSansSerifFontFamily); + } + if (webViewSerifFontFamily != null) settings.setSerifFontFamily(webViewSerifFontFamily); + if (webViewCursiveFontFamily != null) + settings.setCursiveFontFamily(webViewCursiveFontFamily); + if (webViewFantasyFontFamily != null) + settings.setFantasyFontFamily(webViewFantasyFontFamily); + if (webViewMinimumFontSize != null) settings.setMinimumFontSize(webViewMinimumFontSize); + if (webViewMinimumLogicalFontSize != null) { + settings.setMinimumLogicalFontSize(webViewMinimumLogicalFontSize); + } + if (webViewDefaultFontSize != null) settings.setDefaultFontSize(webViewDefaultFontSize); + if (webViewDefaultFixedFontSize != null) { + settings.setDefaultFixedFontSize(webViewDefaultFixedFontSize); + } + if (webViewLoadsImagesAutomatically != null) { + settings.setLoadsImagesAutomatically(webViewLoadsImagesAutomatically); + } + if (webViewBlockNetworkImage != null) + settings.setBlockNetworkImage(webViewBlockNetworkImage); + if (webViewBlockNetworkLoads != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { + settings.setBlockNetworkLoads(webViewBlockNetworkLoads); + } + if (webViewJavaScriptEnabled != null) + settings.setJavaScriptEnabled(webViewJavaScriptEnabled); + if (webViewAllowUniversalAccessFromFileURLs != null + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + settings.setAllowUniversalAccessFromFileURLs(webViewAllowUniversalAccessFromFileURLs); + } + if (webViewAllowFileAccessFromFileURLs != null + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + settings.setAllowFileAccessFromFileURLs(webViewAllowFileAccessFromFileURLs); + } + if (webViewGeolocationDatabasePath != null) { + settings.setGeolocationDatabasePath(webViewGeolocationDatabasePath); + } + if (webViewAppCacheEnabled != null + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (webViewAppCacheEnabled) { + settings.setCacheMode(WebSettings.LOAD_DEFAULT); + } else { + settings.setCacheMode(WebSettings.LOAD_NO_CACHE); + } + } + if (webViewDatabaseEnabled != null) settings.setDatabaseEnabled(webViewDatabaseEnabled); + if (webViewDomStorageEnabled != null) + settings.setDomStorageEnabled(webViewDomStorageEnabled); + if (webViewGeolocationEnabled != null) { + settings.setGeolocationEnabled(webViewGeolocationEnabled); + } + if (webViewJavaScriptCanOpenWindowsAutomatically != null) { + settings.setJavaScriptCanOpenWindowsAutomatically( + webViewJavaScriptCanOpenWindowsAutomatically); + } + if (webViewDefaultTextEncodingName != null) { + settings.setDefaultTextEncodingName(webViewDefaultTextEncodingName); + } + if (webViewUserAgentString != null) { + settings.setUserAgentString(webViewUserAgentAppend? settings.getUserAgentString() + " " + webViewUserAgentString: webViewUserAgentString); + } + if (webViewNeedInitialFocus != null) + settings.setNeedInitialFocus(webViewNeedInitialFocus); + if (webViewCacheMode != null) settings.setCacheMode(webViewCacheMode); + if (webViewMixedContentMode != null + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + settings.setMixedContentMode(webViewMixedContentMode); + } + if (webViewOffscreenPreRaster != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + settings.setOffscreenPreRaster(webViewOffscreenPreRaster); + } + + // // Other webview options + // webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY); + // webView.setScrollbarFadingEnabled(false); + // //Additional Webview Properties + // webView.setSoundEffectsEnabled(true); + // webView.setHorizontalFadingEdgeEnabled(false); + // webView.setKeepScreenOn(true); + // webView.setScrollbarFadingEnabled(true); + // webView.setVerticalFadingEdgeEnabled(false); + } + + { // Divider + gradient.setVisibility(showDivider && gradientDivider ? View.VISIBLE : View.GONE); + divider.setVisibility(showDivider && !gradientDivider ? View.VISIBLE : View.GONE); + if (gradientDivider) { + int dividerWidth = DisplayUtil.getWidth(); + Bitmap bitmap = + BitmapHelper.getGradientBitmap(dividerWidth, (int) dividerHeight, dividerColor); + BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); + ViewUtil.setBackground(gradient, drawable); + + CoordinatorLayout.LayoutParams params = + (CoordinatorLayout.LayoutParams) gradient.getLayoutParams(); + params.height = (int) dividerHeight; + gradient.setLayoutParams(params); + } else { + divider.setBackgroundColor(dividerColor); + + LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) divider.getLayoutParams(); + params.height = (int) dividerHeight; + divider.setLayoutParams(params); + } + } + + { // ProgressBar + progressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE); + progressBar.getProgressDrawable().setColorFilter(progressBarColor, PorterDuff.Mode.SRC_IN); + progressBar.setMinimumHeight((int) progressBarHeight); + CoordinatorLayout.LayoutParams params = + new CoordinatorLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + (int) progressBarHeight); + float toolbarHeight = toolbarVisible? getResources().getDimension(R.dimen.toolbarHeight): 0; + switch (progressBarPosition) { + case TOP_OF_TOOLBAR: + params.setMargins(0, 0, 0, 0); + break; + case BOTTOM_OF_TOOLBAR: + params.setMargins(0, (int) toolbarHeight - (int) progressBarHeight, 0, 0); + break; + case TOP_OF_WEBVIEW: + params.setMargins(0, (int) toolbarHeight, 0, 0); + break; + case BOTTOM_OF_WEBVIEW: + params.setMargins(0, DisplayUtil.getHeight() - (int) progressBarHeight, 0, 0); + break; + } + progressBar.setLayoutParams(params); + } + + { // Menu + GradientDrawable drawable = new GradientDrawable(); + drawable.setCornerRadius(getResources().getDimension(R.dimen.defaultMenuCornerRadius)); + drawable.setColor(menuColor); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + menuBackground.setBackground(drawable); + } else { + menuBackground.setBackgroundDrawable(drawable); + } + + shadowLayout.setShadowColor(menuDropShadowColor); + shadowLayout.setShadowSize(menuDropShadowSize); + + RelativeLayout.LayoutParams params = + new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + int margin = + (int) (getResources().getDimension(R.dimen.defaultMenuLayoutMargin) - menuDropShadowSize); + params.setMargins(0, margin, margin, 0); + params.addRule(RelativeLayout.ALIGN_PARENT_TOP); + params.addRule(rtl ? RelativeLayout.ALIGN_PARENT_LEFT : RelativeLayout.ALIGN_PARENT_RIGHT); + shadowLayout.setLayoutParams(params); + + menuRefresh.setVisibility(showMenuRefresh ? View.VISIBLE : View.GONE); + menuRefresh.setBackgroundResource(menuSelector); + menuRefresh.setGravity(menuTextGravity); + menuRefreshTv.setText(stringResRefresh); + menuRefreshTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, menuTextSize); + menuRefreshTv.setTypeface(TypefaceHelper.get(this, menuTextFont)); + menuRefreshTv.setTextColor(menuTextColor); + menuRefreshTv.setPadding((int) menuTextPaddingLeft, 0, (int) menuTextPaddingRight, 0); + + menuFind.setVisibility(showMenuFind ? View.VISIBLE : View.GONE); + menuFind.setBackgroundResource(menuSelector); + menuFind.setGravity(menuTextGravity); + menuFindTv.setText(stringResFind); + menuFindTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, menuTextSize); + menuFindTv.setTypeface(TypefaceHelper.get(this, menuTextFont)); + menuFindTv.setTextColor(menuTextColor); + menuFindTv.setPadding((int) menuTextPaddingLeft, 0, (int) menuTextPaddingRight, 0); + + menuShareVia.setVisibility(showMenuShareVia ? View.VISIBLE : View.GONE); + menuShareVia.setBackgroundResource(menuSelector); + menuShareVia.setGravity(menuTextGravity); + menuShareViaTv.setText(stringResShareVia); + menuShareViaTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, menuTextSize); + menuShareViaTv.setTypeface(TypefaceHelper.get(this, menuTextFont)); + menuShareViaTv.setTextColor(menuTextColor); + menuShareViaTv.setPadding((int) menuTextPaddingLeft, 0, (int) menuTextPaddingRight, 0); + + menuCopyLink.setVisibility(showMenuCopyLink ? View.VISIBLE : View.GONE); + menuCopyLink.setBackgroundResource(menuSelector); + menuCopyLink.setGravity(menuTextGravity); + menuCopyLinkTv.setText(stringResCopyLink); + menuCopyLinkTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, menuTextSize); + menuCopyLinkTv.setTypeface(TypefaceHelper.get(this, menuTextFont)); + menuCopyLinkTv.setTextColor(menuTextColor); + menuCopyLinkTv.setPadding((int) menuTextPaddingLeft, 0, (int) menuTextPaddingRight, 0); + + menuOpenWith.setVisibility(showMenuOpenWith ? View.VISIBLE : View.GONE); + menuOpenWith.setBackgroundResource(menuSelector); + menuOpenWith.setGravity(menuTextGravity); + menuOpenWithTv.setText(stringResOpenWith); + menuOpenWithTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, menuTextSize); + menuOpenWithTv.setTypeface(TypefaceHelper.get(this, menuTextFont)); + menuOpenWithTv.setTextColor(menuTextColor); + menuOpenWithTv.setPadding((int) menuTextPaddingLeft, 0, (int) menuTextPaddingRight, 0); + + for (final CustomMenu customMenu: customMenus) { + View customMenuRoot = LayoutInflater.from(AwesomeWebViewActivity.this).inflate(R.layout.view_custom_menu, null, false); + LinearLayout customMenuLayout = customMenuRoot.findViewById(R.id.customMenuLayout); + TextView customMenuTv = customMenuLayout.findViewById(R.id.customMenu); + customMenuLayout.setBackgroundResource(menuSelector); + customMenuLayout.setGravity(menuTextGravity); + customMenuTv.setText(customMenu.getTitleRes()); + customMenuTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, menuTextSize); + customMenuTv.setTypeface(TypefaceHelper.get(this, menuTextFont)); + customMenuTv.setTextColor(menuTextColor); + customMenuTv.setPadding((int) menuTextPaddingLeft, 0, (int) menuTextPaddingRight, 0); + + customMenuLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BroadCastManager.onCustomMenuClick(AwesomeWebViewActivity.this, key, customMenu.getCode()); + hideMenu(); + } + }); + + menuBackground.addView(customMenuRoot, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + } + } + + protected WebView buildWebView() { + return new VideoEnabledWebView(this); + } + + protected WebChromeClient buildWebChromeClient() { + // Initialize the VideoEnabledWebChromeClient and set event handlers + View nonVideoLayout = webLayout; // Your own view, read class comments + ViewGroup videoLayout = (ViewGroup) findViewById(R.id.videoLayout); // Your own view, read class comments + //noinspection all + View loadingView = getLayoutInflater().inflate(R.layout.view_loading_video, null); // Your own view, read class comments + MyWebChromeClient webChromeClient = new MyWebChromeClient(nonVideoLayout, videoLayout, loadingView, webView); + webChromeClient.setOnToggledFullscreen(new VideoEnabledWebChromeClient.ToggledFullscreenCallback() { + @Override + public void toggledFullscreen(boolean fullscreen) { + // Your code to handle the full-screen change, for example showing and hiding the title bar. Example: + if (fullscreen) { + WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; + attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + getWindow().setAttributes(attrs); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + //noinspection all + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); + } + } else { + WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN; + attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + getWindow().setAttributes(attrs); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + //noinspection all + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + } + + } + }); + return webChromeClient; + } + + protected WebViewClient buildWebViewClient() { + return new MyWebViewClient(); + } + + protected void injectCookie() { + if (injectCookies != null && injectCookies.size() > 0) { + // https://blog.csdn.net/juhua2012/article/details/52249720 + CookieSyncManager.createInstance(this); + CookieSyncManager.getInstance().sync(); + + CookieManager cookieManager = CookieManager.getInstance(); + cookieManager.setAcceptCookie(true); + for (String url: injectCookies.keySet()) { + Map cookie = injectCookies.get(url); + if (cookie == null || cookie.size() == 0) { + continue; + } + for (String key: cookie.keySet()) { + String value = cookie.get(key); + String cookieStr = key + "=" + value; + cookieManager.setCookie(url, cookieStr); + } + } + CookieSyncManager.getInstance().sync(); + } + } + + protected void addJavascriptInterface() { + CommonJsHelper.getInstance().addJavascriptInterface(webView); + } + + protected void load() { + if (data != null) { + webView.loadData(data, mimeType, encoding); + } else if (url != null) { + if (extraHeaders == null) { + webView.loadUrl(url); + } else { + webView.loadUrl(url, extraHeaders); + } + } + } + + protected int getMaxWidth() { + if (forward.getVisibility() == View.VISIBLE) { + return DisplayUtil.getWidth() - UnitConverter.dpToPx(100); + } else { + return DisplayUtil.getWidth() - UnitConverter.dpToPx(52); + } + } + + protected void updateIcon(ImageButton icon, @DrawableRes int drawableRes) { + StateListDrawable states = new StateListDrawable(); + { + Bitmap bitmap = BitmapHelper.getColoredBitmap(this, drawableRes, iconPressedColor); + BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); + states.addState(new int[]{android.R.attr.state_pressed}, drawable); + } + { + Bitmap bitmap = BitmapHelper.getColoredBitmap(this, drawableRes, iconDisabledColor); + BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); + states.addState(new int[]{-android.R.attr.state_enabled}, drawable); + } + { + Bitmap bitmap = BitmapHelper.getColoredBitmap(this, drawableRes, iconDefaultColor); + BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); + states.addState(new int[]{}, drawable); + } + icon.setImageDrawable(states); + + // int[][] states = new int[][]{ + // new int[]{-android.R.attr.state_enabled}, // disabled + // new int[]{android.R.attr.state_pressed}, // pressed + // new int[]{} // default + // }; + // + // int[] colors = new int[]{ + // iconDisabledColor, + // iconPressedColor, + // iconDefaultColor + // }; + // + // ColorStateList colorStateList = new ColorStateList(states, colors); + // + // Drawable drawable = ContextCompat.getDrawable(this, drawableRes); + // if (APILevel.require(21)) { + // VectorDrawable vectorDrawable = (VectorDrawable) drawable; + // vectorDrawable.setTintList(colorStateList); + // icon.setImageDrawable(vectorDrawable); + // } else { + // VectorDrawableCompat vectorDrawable = (VectorDrawableCompat) drawable; + // vectorDrawable.setTintList(colorStateList); + // icon.setImageDrawable(vectorDrawable); + // } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + initializeOptions(); + + setContentView(R.layout.awesome_web_view); + bindViews(); + layoutViews(); + initializeViews(); + injectCookie(); + addJavascriptInterface(); + load(); + } + + @Override + public void onBackPressed() { + if (webChromeClient instanceof MyWebChromeClient && ((MyWebChromeClient)webChromeClient).onBackPressed()) { + return; + } + if (menuLayout.getVisibility() == View.VISIBLE) { + hideMenu(); + } else if (backPressToClose || !webView.canGoBack()) { + exitActivity(); + } else { + webView.goBack(); + } + } + + @Override + public void onClick(View v) { + int viewId = v.getId(); + if (viewId == R.id.close) { + if (rtl) { + showMenu(); + } else { + exitActivity(); + } + } else if (viewId == R.id.back) { + if (rtl) { + webView.goForward(); + } else { + webView.goBack(); + } + } else if (viewId == R.id.forward) { + if (rtl) { + webView.goBack(); + } else { + webView.goForward(); + } + } else if (viewId == R.id.more) { + if (rtl) { + exitActivity(); + } else { + showMenu(); + } + } else if (viewId == R.id.menuLayout) { + hideMenu(); + } else if (viewId == R.id.menuRefresh) { + webView.reload(); + hideMenu(); + } else if (viewId == R.id.menuFind) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + webView.showFindDialog("", true); + hideMenu(); + } else if (viewId == R.id.menuShareVia) { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, webView.getUrl()); + sendIntent.setType("text/plain"); + startActivity(Intent.createChooser(sendIntent, getResources().getString(stringResShareVia))); + + hideMenu(); + } else if (viewId == R.id.menuCopyLink) { + ClipboardManagerUtil.setText(webView.getUrl()); + + Snackbar snackbar = Snackbar.make(coordinatorLayout, getString(stringResCopiedToClipboard), + Snackbar.LENGTH_LONG); + View snackbarView = snackbar.getView(); + snackbarView.setBackgroundColor(toolbarColor); + if (snackbarView instanceof ViewGroup) updateChildTextView((ViewGroup) snackbarView); + snackbar.show(); + + hideMenu(); + } else if (viewId == R.id.menuOpenWith) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webView.getUrl())); + startActivity(browserIntent); + + hideMenu(); + } + } + + protected void updateChildTextView(ViewGroup viewGroup) { + if (viewGroup == null || viewGroup.getChildCount() == 0) return; + + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View view = viewGroup.getChildAt(i); + if (view instanceof TextView) { + TextView textView = (TextView) view; + textView.setTextColor(titleColor); + textView.setTypeface(TypefaceHelper.get(this, titleFont)); + textView.setLineSpacing(0, 1.1f); + textView.setIncludeFontPadding(false); + } + + if (view instanceof ViewGroup) updateChildTextView((ViewGroup) view); + } + } + + protected void showMenu() { + menuLayout.setVisibility(View.VISIBLE); + Animation popupAnim = AnimationUtils.loadAnimation(this, R.anim.popup_flyout_show); + shadowLayout.startAnimation(popupAnim); + } + + protected void hideMenu() { + Animation popupAnim = AnimationUtils.loadAnimation(this, R.anim.popup_flyout_hide); + shadowLayout.startAnimation(popupAnim); + popupAnim.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + menuLayout.setVisibility(View.GONE); + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + }); + } + + protected void exitActivity() { + super.onBackPressed(); + overridePendingTransition(animationCloseEnter, animationCloseExit); + } + + protected void requestCenterLayout() { + int maxWidth; + if (webView.canGoBack() || webView.canGoForward()) { + maxWidth = DisplayUtil.getWidth() - UnitConverter.dpToPx(48) * 4; + } else { + maxWidth = DisplayUtil.getWidth() - UnitConverter.dpToPx(48) * 2; + } + + title.setMaxWidth(maxWidth); + urlTv.setMaxWidth(maxWidth); + title.requestLayout(); + urlTv.requestLayout(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + layoutViews(); + } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { + layoutViews(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + BroadCastManager.unregister(AwesomeWebViewActivity.this, key); + if (webView == null) return; + if (APILevel.require(11)) webView.onPause(); + destroyWebView(); + } + + // Wait for zoom control to fade away + // https://code.google.com/p/android/issues/detail?id=15694 + // http://stackoverflow.com/a/5966151/1797648 + protected void destroyWebView() { + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + if (webView != null) webView.destroy(); + } + }, ViewConfiguration.getZoomControlsTimeout() + 1000L); + } + + @Override + public boolean handleMessage(Message msg) { + if (msg.what == MSG_CLICK_ON_URL){ + handler.removeMessages(MSG_CLICK_ON_WEBVIEW); + return true; + } + if (msg.what == MSG_CLICK_ON_WEBVIEW){ + final WebView.HitTestResult hitTestResult = webView.getHitTestResult(); + // 如果是图片类型或者是带有图片链接的类型 + if (hitTestResult.getType() == WebView.HitTestResult.IMAGE_TYPE || + hitTestResult.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { + BroadCastManager.onClickImage(AwesomeWebViewActivity.this, key, hitTestResult.getExtra()); + } + return true; + } + return false; + } + + public class MyWebChromeClient extends VideoEnabledWebChromeClient { + + public MyWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView, WebView webView) { + super(activityNonVideoView, activityVideoView, loadingView, webView); + } + + @Override + public void onProgressChanged(WebView view, int progress) { + BroadCastManager.onProgressChanged(AwesomeWebViewActivity.this, key, progress); + + if (progress == 100) progress = 0; + progressBar.setProgress(progress); + } + + @Override + public void onReceivedTitle(WebView view, String title) { + BroadCastManager.onReceivedTitle(AwesomeWebViewActivity.this, key, title); + } + + @Override + public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) { + BroadCastManager.onReceivedTouchIconUrl(AwesomeWebViewActivity.this, key, url, precomposed); + } + + @Override + public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback callback) { + PermissionHelper.CheckPermissions(AwesomeWebViewActivity.this, new PermissionHelper.CheckPermissionListener() { + @Override + public void onAllGranted(boolean sync) { + callback.invoke(origin, true, true); + } + + @Override + public void onPartlyGranted(List permissionsDenied, boolean sync) { + callback.invoke(origin, false, false); + } + }, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public void onPermissionRequest(final PermissionRequest request) { + for (String res : request.getResources()) { + if (res.equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE)) { + if (!webViewAudioEnabled) { + request.deny(); + return; + } + } + if (res.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) { + if (!webViewCameraEnabled) { + request.deny(); + return; + } + } + } + + PermissionHelper.CheckPermissions(AwesomeWebViewActivity.this, new PermissionHelper.CheckPermissionListener() { + @Override + public void onAllGranted(boolean sync) { + request.grant(request.getResources()); + } + + @Override + public void onPartlyGranted(List permissionsDenied, boolean sync) { + request.deny(); + } + }, parsePermission(request.getResources())); + } + + //Handling input[type="file"] requests for android API 16+ + public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) { + handler.sendEmptyMessage(MSG_CLICK_ON_URL); + + if (!fileChooserEnabled) { + uploadMsg.onReceiveValue(null); + return; + } + filePickerFileMessage = uploadMsg; + Intent i = new Intent(Intent.ACTION_GET_CONTENT); + i.addCategory(Intent.CATEGORY_OPENABLE); + i.setType(acceptType); + startActivityForResult(Intent.createChooser(i, getResources().getString(stringResFileChooserTitle)), FILE_PICKER_REQ_CODE); + } + + //Handling input[type="file"] requests for android API 21+ + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, final FileChooserParams fileChooserParams) { + handler.sendEmptyMessage(MSG_CLICK_ON_URL); + + if (!fileChooserEnabled) { + filePathCallback.onReceiveValue(null); + return true; + } + if (filePickerFilePath != null) { + filePickerFilePath.onReceiveValue(null); + } + filePickerFilePath = filePathCallback; + + //Checking permission for storage and camera for writing and uploading images + String[] perms = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}; + PermissionHelper.CheckPermissions(AwesomeWebViewActivity.this, new PermissionHelper.CheckPermissionListener() { + @Override + public void onAllGranted(boolean sync) { + final Intent takePictureIntent = createCameraCaptureIntent(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP? fileChooserParams.getAcceptTypes(): null); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && fileChooserParams.isCaptureEnabled() && fileChooserParams.getMode() == FileChooserParams.MODE_OPEN) { + // capture="camera" and without multiple + if (takePictureIntent != null) { + startActivityForResult(takePictureIntent, FILE_PICKER_REQ_CODE); + } else { + if (filePickerFilePath != null) { + filePickerFilePath.onReceiveValue(null); + filePickerFilePath = null; + } + } + return; + } + final Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT); + contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE); + contentSelectionIntent.setType(FILE_TYPE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + contentSelectionIntent.putExtra(Intent.EXTRA_MIME_TYPES, fileChooserParams.getAcceptTypes()); + } + Intent[] intentArray; + if (takePictureIntent != null) { + intentArray = new Intent[]{takePictureIntent}; + } else { + intentArray = new Intent[0]; + } + Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); + chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent); + chooserIntent.putExtra(Intent.EXTRA_TITLE, getResources().getString(stringResFileChooserTitle)); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); + startActivityForResult(chooserIntent, FILE_PICKER_REQ_CODE); + } + + @Override + public void onPartlyGranted(List permissionsDenied, boolean sync) { + if (filePickerFilePath != null) { + filePickerFilePath.onReceiveValue(null); + filePickerFilePath = null; + } + } + }, perms); + return true; + } + } + + private Intent createCameraCaptureIntent(String[] mimeTypes) { + boolean isVideo = false; + if (mimeTypes != null && mimeTypes.length == 1 && mimeTypes[0] != null && mimeTypes[0].startsWith("video")) { + isVideo = true; + } + Intent takePictureIntent = new Intent(isVideo? MediaStore.ACTION_VIDEO_CAPTURE: MediaStore.ACTION_IMAGE_CAPTURE); + if (takePictureIntent.resolveActivity(AwesomeWebViewActivity.this.getPackageManager()) != null) { + File imageVideoFile = null; + try { + imageVideoFile = createImageOrVideo(isVideo); + } catch (IOException ex) { +// Log.e("", "Image file creation failed", ex); + ex.printStackTrace(); + } + if (imageVideoFile != null) { + filePickerCamMessage = "file:" + imageVideoFile.getAbsolutePath(); + + Uri photoUri = FileProvider.getUriForFile( + this, + getPackageName() + ".awesome_web_view.file_provider", + imageVideoFile); + + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); + } else { + takePictureIntent = null; + } + } + return takePictureIntent; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { +// getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); +// getWindow().setStatusBarColor(getResources().getColor(R.color.colorPrimary)); + Uri[] results = null; + if (resultCode == Activity.RESULT_OK) { + if (requestCode == FILE_PICKER_REQ_CODE) { + if (null == filePickerFilePath) { + return; + } + if (intent == null || intent.getDataString() == null) { + if (filePickerCamMessage != null) { + results = new Uri[]{Uri.parse(filePickerCamMessage)}; + } + } else { + String dataString = intent.getDataString(); + if (dataString != null) { + results = new Uri[]{Uri.parse(dataString)}; + } + } + } + } + filePickerFilePath.onReceiveValue(results); + filePickerFilePath = null; + } else { + if (requestCode == FILE_PICKER_REQ_CODE) { + if (null == filePickerFileMessage) return; + Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData(); + filePickerFileMessage.onReceiveValue(result); + filePickerFileMessage = null; + } + } + } + + //Creating image or video file for upload + protected File createImageOrVideo(boolean isVideo) throws IOException { + @SuppressLint("SimpleDateFormat") + String file_name = new SimpleDateFormat("yyyy_mm_ss").format(new Date()); + String new_name = "file_" + file_name + "_"; + File sd_directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + return File.createTempFile(new_name, isVideo? ".mp4": ".jpg", sd_directory); + } + + public class MyWebViewClient extends WebViewClient { + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + BroadCastManager.onPageStarted(AwesomeWebViewActivity.this, key, url); + if (!url.contains("docs.google.com") && url.endsWith(".pdf")) { + webView.loadUrl("http://docs.google.com/gview?embedded=true&url=" + url); + } + } + + @Override + public void onPageFinished(WebView view, String url) { + BroadCastManager.onPageFinished(AwesomeWebViewActivity.this, key, url); + + if (updateTitleFromHtml) title.setText(view.getTitle()); + urlTv.setText(UrlParser.getHost(url)); + requestCenterLayout(); + + if (view.canGoBack() || view.canGoForward()) { + back.setVisibility(showIconBack ? View.VISIBLE : View.GONE); + forward.setVisibility(showIconForward ? View.VISIBLE : View.GONE); + back.setEnabled(!disableIconBack && (rtl ? view.canGoForward() : view.canGoBack())); + forward.setEnabled(!disableIconForward && (rtl ? view.canGoBack() : view.canGoForward())); + } else { + back.setVisibility(View.GONE); + forward.setVisibility(View.GONE); + } + + if (injectJavaScript != null) { + if (injectJavaScriptMainPage && !url.equals(AwesomeWebViewActivity.this.url)) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + webView.evaluateJavascript(injectJavaScript, null); + } else { + webView.loadUrl(injectJavaScript); + } + } + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + handler.sendEmptyMessage(MSG_CLICK_ON_URL); + + if (url.endsWith(".mp4")) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.parse(url), "video/*"); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + view.getContext().startActivity(intent); + // If we return true, onPageStarted, onPageFinished won't be called. + return true; + } else if (url.startsWith("tel:") || url.startsWith("sms:") || url.startsWith("smsto:") || url + .startsWith("mms:") || url.startsWith("mmsto:")) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + view.getContext().startActivity(intent); + return true; // If we return true, onPageStarted, onPageFinished won't be called. + } + /******************************************************* + * Added in support for mailto: + *******************************************************/ + else if (url.startsWith("mailto:")) { + + MailTo mt = MailTo.parse(url); + + Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND); + + emailIntent.setType("text/html"); + emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{mt.getTo()}); + emailIntent.putExtra(Intent.EXTRA_SUBJECT, mt.getSubject()); + emailIntent.putExtra(Intent.EXTRA_CC, mt.getCc()); + emailIntent.putExtra(Intent.EXTRA_TEXT, mt.getBody()); + + startActivity(emailIntent); + + return true; + } else if (url.startsWith("http") || url.startsWith("https") || url.startsWith("ftp")) { + if (extraHeaders == null || extraHeadersMainPage && !url.equals(AwesomeWebViewActivity.this.url)) { + return super.shouldOverrideUrlLoading(view, url); + } else { + view.loadUrl(url, extraHeaders); + return true; + } + } else { + if (webViewAppJumpEnabled) { + try { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + view.getContext().startActivity(intent); + return true; // If we return true, onPageStarted, onPageFinished won't be called. + } catch (Exception exception) { + exception.printStackTrace(); + return true; + } + } else { + return super.shouldOverrideUrlLoading(view, url); + } + } + } + + @Override + public void onLoadResource(WebView view, String url) { + BroadCastManager.onLoadResource(AwesomeWebViewActivity.this, key, url); + } + + @Override + public void onPageCommitVisible(WebView view, String url) { + BroadCastManager.onPageCommitVisible(AwesomeWebViewActivity.this, key, url); + } + } + + protected String[] parsePermission(String[] resource) { + List permissions = new ArrayList<>(); + for (String res : resource) { + if (res.equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE)) { + permissions.add(Manifest.permission.RECORD_AUDIO); + } + if (res.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) { + permissions.add(Manifest.permission.CAMERA); + } + } + + String[] result = new String[permissions.size()]; + for (int i = 0; i < permissions.size(); i++) { + result[i] = permissions.get(i); + } + return result; + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/enums/Position.java b/library/src/main/java/com/wuadam/awesomewebview/enums/Position.java new file mode 100644 index 0000000..62ad266 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/enums/Position.java @@ -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; +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/helpers/Base64ImgHelper.java b/library/src/main/java/com/wuadam/awesomewebview/helpers/Base64ImgHelper.java new file mode 100644 index 0000000..76af518 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/helpers/Base64ImgHelper.java @@ -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; + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/helpers/BitmapHelper.java b/library/src/main/java/com/wuadam/awesomewebview/helpers/BitmapHelper.java new file mode 100644 index 0000000..c1ad26c --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/helpers/BitmapHelper.java @@ -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; + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/helpers/ColorHelper.java b/library/src/main/java/com/wuadam/awesomewebview/helpers/ColorHelper.java new file mode 100644 index 0000000..47331b3 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/helpers/ColorHelper.java @@ -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); + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/helpers/DownPicUtil.java b/library/src/main/java/com/wuadam/awesomewebview/helpers/DownPicUtil.java new file mode 100644 index 0000000..b2cee87 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/helpers/DownPicUtil.java @@ -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(){ + 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(); + } +} \ No newline at end of file diff --git a/library/src/main/java/com/wuadam/awesomewebview/helpers/FileProvider4WebView.java b/library/src/main/java/com/wuadam/awesomewebview/helpers/FileProvider4WebView.java new file mode 100644 index 0000000..adba271 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/helpers/FileProvider4WebView.java @@ -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 { +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/helpers/FormatHelper.java b/library/src/main/java/com/wuadam/awesomewebview/helpers/FormatHelper.java new file mode 100644 index 0000000..b282ad3 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/helpers/FormatHelper.java @@ -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; + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/helpers/Md5Helper.java b/library/src/main/java/com/wuadam/awesomewebview/helpers/Md5Helper.java new file mode 100644 index 0000000..924b0ac --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/helpers/Md5Helper.java @@ -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); + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/helpers/PermissionHelper.java b/library/src/main/java/com/wuadam/awesomewebview/helpers/PermissionHelper.java new file mode 100644 index 0000000..19ad39f --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/helpers/PermissionHelper.java @@ -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 getGrantedPermissions(Context context, String... permissionName) { + List 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 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>() { +// @Override +// public void onAction(List permissions) { +// // 权限申请成功回调。 +// if (checkPermissionListener != null) { +// checkPermissionListener.onAllGranted(false); +// } +// } +// }) +// .onDenied(new Action>() { +// @Override +// public void onAction(List permissions) { +// if (checkPermissionListener != null) { +// checkPermissionListener.onPartlyGranted(permissions, false); +// } +// } +// }) +// .start(); + } + } + +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/helpers/TypefaceHelper.java b/library/src/main/java/com/wuadam/awesomewebview/helpers/TypefaceHelper.java new file mode 100644 index 0000000..7d33cec --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/helpers/TypefaceHelper.java @@ -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 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); + } + } +} \ No newline at end of file diff --git a/library/src/main/java/com/wuadam/awesomewebview/helpers/UrlParser.java b/library/src/main/java/com/wuadam/awesomewebview/helpers/UrlParser.java new file mode 100644 index 0000000..34e3396 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/helpers/UrlParser.java @@ -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; + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/jsInterface/BaseJsInterface.java b/library/src/main/java/com/wuadam/awesomewebview/jsInterface/BaseJsInterface.java new file mode 100644 index 0000000..b3c2b2f --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/jsInterface/BaseJsInterface.java @@ -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(); + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/jsInterface/CommonJsHelper.java b/library/src/main/java/com/wuadam/awesomewebview/jsInterface/CommonJsHelper.java new file mode 100644 index 0000000..7c276c5 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/jsInterface/CommonJsHelper.java @@ -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 { + 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, String>> interfacesInternal = new ArrayList<>(0); + + /** + * add new JS interface + * @param ifc + * @param bridge + */ + public void addJavascriptInterface(Class ifc, String bridge) { + interfacesInternal.add(Pair.create(ifc, bridge)); + } + + /** + * do not call, only for internal + * @param webView + */ + public void addJavascriptInterface(WebView webView) { + for (Pair, 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(); + } + } + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/jsInterface/VideoJsHelper.java b/library/src/main/java/com/wuadam/awesomewebview/jsInterface/VideoJsHelper.java new file mode 100644 index 0000000..77cb4b7 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/jsInterface/VideoJsHelper.java @@ -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; + } + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/listeners/BroadCastManager.java b/library/src/main/java/com/wuadam/awesomewebview/listeners/BroadCastManager.java new file mode 100644 index 0000000..8c61cd8 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/listeners/BroadCastManager.java @@ -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 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 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 + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/listeners/WebViewListener.java b/library/src/main/java/com/wuadam/awesomewebview/listeners/WebViewListener.java new file mode 100644 index 0000000..292997a --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/listeners/WebViewListener.java @@ -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) { + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/objects/CustomMenu.java b/library/src/main/java/com/wuadam/awesomewebview/objects/CustomMenu.java new file mode 100644 index 0000000..f44c5bc --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/objects/CustomMenu.java @@ -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; + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/views/ShadowLayout.java b/library/src/main/java/com/wuadam/awesomewebview/views/ShadowLayout.java new file mode 100644 index 0000000..334a950 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/views/ShadowLayout.java @@ -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; + } +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/views/VideoEnabledWebChromeClient.java b/library/src/main/java/com/wuadam/awesomewebview/views/VideoEnabledWebChromeClient.java new file mode 100644 index 0000000..c466774 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/views/VideoEnabledWebChromeClient.java @@ -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; + } + } + +} diff --git a/library/src/main/java/com/wuadam/awesomewebview/views/VideoEnabledWebView.java b/library/src/main/java/com/wuadam/awesomewebview/views/VideoEnabledWebView.java new file mode 100644 index 0000000..63f4b66 --- /dev/null +++ b/library/src/main/java/com/wuadam/awesomewebview/views/VideoEnabledWebView.java @@ -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 additionalHttpHeaders) + { + videoJsHelper.addJavascriptInterface(this); + super.loadUrl(url, additionalHttpHeaders); + } + +} diff --git a/library/src/main/res/anim/accelerate_cubic.xml b/library/src/main/res/anim/accelerate_cubic.xml new file mode 100755 index 0000000..f5b6aff --- /dev/null +++ b/library/src/main/res/anim/accelerate_cubic.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/library/src/main/res/anim/accelerate_quart.xml b/library/src/main/res/anim/accelerate_quart.xml new file mode 100755 index 0000000..37f44f9 --- /dev/null +++ b/library/src/main/res/anim/accelerate_quart.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/library/src/main/res/anim/accelerate_quint.xml b/library/src/main/res/anim/accelerate_quint.xml new file mode 100755 index 0000000..06c4040 --- /dev/null +++ b/library/src/main/res/anim/accelerate_quint.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/library/src/main/res/anim/activity_close_enter.xml b/library/src/main/res/anim/activity_close_enter.xml new file mode 100755 index 0000000..8dfddbf --- /dev/null +++ b/library/src/main/res/anim/activity_close_enter.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/activity_close_exit.xml b/library/src/main/res/anim/activity_close_exit.xml new file mode 100755 index 0000000..50c60cc --- /dev/null +++ b/library/src/main/res/anim/activity_close_exit.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/activity_open_enter.xml b/library/src/main/res/anim/activity_open_enter.xml new file mode 100755 index 0000000..1cc5fcb --- /dev/null +++ b/library/src/main/res/anim/activity_open_enter.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/activity_open_exit.xml b/library/src/main/res/anim/activity_open_exit.xml new file mode 100755 index 0000000..fd61ee4 --- /dev/null +++ b/library/src/main/res/anim/activity_open_exit.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/decelerate_cubic.xml b/library/src/main/res/anim/decelerate_cubic.xml new file mode 100755 index 0000000..348044f --- /dev/null +++ b/library/src/main/res/anim/decelerate_cubic.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/library/src/main/res/anim/decelerate_quart.xml b/library/src/main/res/anim/decelerate_quart.xml new file mode 100755 index 0000000..4cdfac0 --- /dev/null +++ b/library/src/main/res/anim/decelerate_quart.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/library/src/main/res/anim/decelerate_quint.xml b/library/src/main/res/anim/decelerate_quint.xml new file mode 100755 index 0000000..c47ce53 --- /dev/null +++ b/library/src/main/res/anim/decelerate_quint.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/library/src/main/res/anim/fade_in_fast.xml b/library/src/main/res/anim/fade_in_fast.xml new file mode 100644 index 0000000..dc922c2 --- /dev/null +++ b/library/src/main/res/anim/fade_in_fast.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/fade_in_medium.xml b/library/src/main/res/anim/fade_in_medium.xml new file mode 100644 index 0000000..98e4ccf --- /dev/null +++ b/library/src/main/res/anim/fade_in_medium.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/fade_out_fast.xml b/library/src/main/res/anim/fade_out_fast.xml new file mode 100644 index 0000000..17409c3 --- /dev/null +++ b/library/src/main/res/anim/fade_out_fast.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/fade_out_medium.xml b/library/src/main/res/anim/fade_out_medium.xml new file mode 100644 index 0000000..f8893b8 --- /dev/null +++ b/library/src/main/res/anim/fade_out_medium.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/fragment_close_enter.xml b/library/src/main/res/anim/fragment_close_enter.xml new file mode 100755 index 0000000..ef981be --- /dev/null +++ b/library/src/main/res/anim/fragment_close_enter.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/fragment_close_exit.xml b/library/src/main/res/anim/fragment_close_exit.xml new file mode 100755 index 0000000..c43fd19 --- /dev/null +++ b/library/src/main/res/anim/fragment_close_exit.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/fragment_open_enter.xml b/library/src/main/res/anim/fragment_open_enter.xml new file mode 100755 index 0000000..6f897b3 --- /dev/null +++ b/library/src/main/res/anim/fragment_open_enter.xml @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/fragment_open_exit.xml b/library/src/main/res/anim/fragment_open_exit.xml new file mode 100755 index 0000000..d90bc08 --- /dev/null +++ b/library/src/main/res/anim/fragment_open_exit.xml @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/hold.xml b/library/src/main/res/anim/hold.xml new file mode 100644 index 0000000..637e548 --- /dev/null +++ b/library/src/main/res/anim/hold.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/modal_activity_close_enter.xml b/library/src/main/res/anim/modal_activity_close_enter.xml new file mode 100755 index 0000000..8dfddbf --- /dev/null +++ b/library/src/main/res/anim/modal_activity_close_enter.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/modal_activity_close_exit.xml b/library/src/main/res/anim/modal_activity_close_exit.xml new file mode 100755 index 0000000..d6c960d --- /dev/null +++ b/library/src/main/res/anim/modal_activity_close_exit.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/modal_activity_open_enter.xml b/library/src/main/res/anim/modal_activity_open_enter.xml new file mode 100755 index 0000000..ca2d551 --- /dev/null +++ b/library/src/main/res/anim/modal_activity_open_enter.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/modal_activity_open_exit.xml b/library/src/main/res/anim/modal_activity_open_exit.xml new file mode 100755 index 0000000..fd61ee4 --- /dev/null +++ b/library/src/main/res/anim/modal_activity_open_exit.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/none.xml b/library/src/main/res/anim/none.xml new file mode 100644 index 0000000..ec54181 --- /dev/null +++ b/library/src/main/res/anim/none.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/library/src/main/res/anim/popup_flyout_hide.xml b/library/src/main/res/anim/popup_flyout_hide.xml new file mode 100644 index 0000000..cd74091 --- /dev/null +++ b/library/src/main/res/anim/popup_flyout_hide.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/library/src/main/res/anim/popup_flyout_show.xml b/library/src/main/res/anim/popup_flyout_show.xml new file mode 100644 index 0000000..1f1782c --- /dev/null +++ b/library/src/main/res/anim/popup_flyout_show.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/library/src/main/res/anim/slide_down.xml b/library/src/main/res/anim/slide_down.xml new file mode 100644 index 0000000..80ee47f --- /dev/null +++ b/library/src/main/res/anim/slide_down.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/slide_left_in.xml b/library/src/main/res/anim/slide_left_in.xml new file mode 100644 index 0000000..761339b --- /dev/null +++ b/library/src/main/res/anim/slide_left_in.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/slide_right_out.xml b/library/src/main/res/anim/slide_right_out.xml new file mode 100644 index 0000000..06d05a8 --- /dev/null +++ b/library/src/main/res/anim/slide_right_out.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/anim/slide_up.xml b/library/src/main/res/anim/slide_up.xml new file mode 100644 index 0000000..aca5d38 --- /dev/null +++ b/library/src/main/res/anim/slide_up.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/drawable-hdpi/back.png b/library/src/main/res/drawable-hdpi/back.png new file mode 100755 index 0000000..992037e Binary files /dev/null and b/library/src/main/res/drawable-hdpi/back.png differ diff --git a/library/src/main/res/drawable-hdpi/close.png b/library/src/main/res/drawable-hdpi/close.png new file mode 100755 index 0000000..0b92400 Binary files /dev/null and b/library/src/main/res/drawable-hdpi/close.png differ diff --git a/library/src/main/res/drawable-hdpi/forward.png b/library/src/main/res/drawable-hdpi/forward.png new file mode 100755 index 0000000..bce266d Binary files /dev/null and b/library/src/main/res/drawable-hdpi/forward.png differ diff --git a/library/src/main/res/drawable-hdpi/more.png b/library/src/main/res/drawable-hdpi/more.png new file mode 100755 index 0000000..2d9096e Binary files /dev/null and b/library/src/main/res/drawable-hdpi/more.png differ diff --git a/library/src/main/res/drawable-mdpi/back.png b/library/src/main/res/drawable-mdpi/back.png new file mode 100755 index 0000000..69d1181 Binary files /dev/null and b/library/src/main/res/drawable-mdpi/back.png differ diff --git a/library/src/main/res/drawable-mdpi/close.png b/library/src/main/res/drawable-mdpi/close.png new file mode 100755 index 0000000..0999958 Binary files /dev/null and b/library/src/main/res/drawable-mdpi/close.png differ diff --git a/library/src/main/res/drawable-mdpi/forward.png b/library/src/main/res/drawable-mdpi/forward.png new file mode 100755 index 0000000..1b2f54f Binary files /dev/null and b/library/src/main/res/drawable-mdpi/forward.png differ diff --git a/library/src/main/res/drawable-mdpi/more.png b/library/src/main/res/drawable-mdpi/more.png new file mode 100755 index 0000000..11e44d3 Binary files /dev/null and b/library/src/main/res/drawable-mdpi/more.png differ diff --git a/library/src/main/res/drawable-v21/selector_dark_theme.xml b/library/src/main/res/drawable-v21/selector_dark_theme.xml new file mode 100644 index 0000000..12fed93 --- /dev/null +++ b/library/src/main/res/drawable-v21/selector_dark_theme.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/drawable-v21/selector_dark_theme_holo.xml b/library/src/main/res/drawable-v21/selector_dark_theme_holo.xml new file mode 100644 index 0000000..6156a62 --- /dev/null +++ b/library/src/main/res/drawable-v21/selector_dark_theme_holo.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/drawable-v21/selector_light_theme.xml b/library/src/main/res/drawable-v21/selector_light_theme.xml new file mode 100644 index 0000000..7729d28 --- /dev/null +++ b/library/src/main/res/drawable-v21/selector_light_theme.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/library/src/main/res/drawable-v21/selector_light_theme_holo.xml b/library/src/main/res/drawable-v21/selector_light_theme_holo.xml new file mode 100644 index 0000000..3ef98bf --- /dev/null +++ b/library/src/main/res/drawable-v21/selector_light_theme_holo.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/drawable-xhdpi/back.png b/library/src/main/res/drawable-xhdpi/back.png new file mode 100755 index 0000000..542f883 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/back.png differ diff --git a/library/src/main/res/drawable-xhdpi/close.png b/library/src/main/res/drawable-xhdpi/close.png new file mode 100755 index 0000000..f663724 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/close.png differ diff --git a/library/src/main/res/drawable-xhdpi/forward.png b/library/src/main/res/drawable-xhdpi/forward.png new file mode 100755 index 0000000..4231572 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/forward.png differ diff --git a/library/src/main/res/drawable-xhdpi/more.png b/library/src/main/res/drawable-xhdpi/more.png new file mode 100755 index 0000000..a92dd36 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/more.png differ diff --git a/library/src/main/res/drawable-xxhdpi/back.png b/library/src/main/res/drawable-xxhdpi/back.png new file mode 100755 index 0000000..0ecc13f Binary files /dev/null and b/library/src/main/res/drawable-xxhdpi/back.png differ diff --git a/library/src/main/res/drawable-xxhdpi/close.png b/library/src/main/res/drawable-xxhdpi/close.png new file mode 100755 index 0000000..30d8b78 Binary files /dev/null and b/library/src/main/res/drawable-xxhdpi/close.png differ diff --git a/library/src/main/res/drawable-xxhdpi/forward.png b/library/src/main/res/drawable-xxhdpi/forward.png new file mode 100755 index 0000000..94944bb Binary files /dev/null and b/library/src/main/res/drawable-xxhdpi/forward.png differ diff --git a/library/src/main/res/drawable-xxhdpi/more.png b/library/src/main/res/drawable-xxhdpi/more.png new file mode 100755 index 0000000..65f92cf Binary files /dev/null and b/library/src/main/res/drawable-xxhdpi/more.png differ diff --git a/library/src/main/res/drawable/back_vector.xml b/library/src/main/res/drawable/back_vector.xml new file mode 100644 index 0000000..b6f19de --- /dev/null +++ b/library/src/main/res/drawable/back_vector.xml @@ -0,0 +1,11 @@ + + + + diff --git a/library/src/main/res/drawable/close_vector.xml b/library/src/main/res/drawable/close_vector.xml new file mode 100644 index 0000000..48f0bd8 --- /dev/null +++ b/library/src/main/res/drawable/close_vector.xml @@ -0,0 +1,11 @@ + + + + diff --git a/library/src/main/res/drawable/forward_vector.xml b/library/src/main/res/drawable/forward_vector.xml new file mode 100644 index 0000000..3c78311 --- /dev/null +++ b/library/src/main/res/drawable/forward_vector.xml @@ -0,0 +1,11 @@ + + + + diff --git a/library/src/main/res/drawable/more_vector.xml b/library/src/main/res/drawable/more_vector.xml new file mode 100644 index 0000000..e8c38cc --- /dev/null +++ b/library/src/main/res/drawable/more_vector.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/library/src/main/res/drawable/progress_drawable.xml b/library/src/main/res/drawable/progress_drawable.xml new file mode 100644 index 0000000..d1097ce --- /dev/null +++ b/library/src/main/res/drawable/progress_drawable.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/drawable/selector_dark_theme.xml b/library/src/main/res/drawable/selector_dark_theme.xml new file mode 100644 index 0000000..ff8f295 --- /dev/null +++ b/library/src/main/res/drawable/selector_dark_theme.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/drawable/selector_light_theme.xml b/library/src/main/res/drawable/selector_light_theme.xml new file mode 100644 index 0000000..bd95455 --- /dev/null +++ b/library/src/main/res/drawable/selector_light_theme.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/layout/awesome_web_view.xml b/library/src/main/res/layout/awesome_web_view.xml new file mode 100644 index 0000000..026f8c5 --- /dev/null +++ b/library/src/main/res/layout/awesome_web_view.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/src/main/res/layout/menus.xml b/library/src/main/res/layout/menus.xml new file mode 100644 index 0000000..3cbadf8 --- /dev/null +++ b/library/src/main/res/layout/menus.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/layout/toolbar_content.xml b/library/src/main/res/layout/toolbar_content.xml new file mode 100644 index 0000000..9f2fc13 --- /dev/null +++ b/library/src/main/res/layout/toolbar_content.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/src/main/res/layout/view_custom_menu.xml b/library/src/main/res/layout/view_custom_menu.xml new file mode 100644 index 0000000..ac53514 --- /dev/null +++ b/library/src/main/res/layout/view_custom_menu.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/layout/view_loading_video.xml b/library/src/main/res/layout/view_loading_video.xml new file mode 100644 index 0000000..442f808 --- /dev/null +++ b/library/src/main/res/layout/view_loading_video.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/library/src/main/res/values-es/strings.xml b/library/src/main/res/values-es/strings.xml new file mode 100644 index 0000000..f308a6d --- /dev/null +++ b/library/src/main/res/values-es/strings.xml @@ -0,0 +1,9 @@ + + + Recargar + Encontrar + Compartir + Copiar enlace + Abrir con + Copiado al portapapeles + \ No newline at end of file diff --git a/library/src/main/res/values-ko/strings.xml b/library/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000..21299d5 --- /dev/null +++ b/library/src/main/res/values-ko/strings.xml @@ -0,0 +1,9 @@ + + + 새로고침 + 찾기 + 주소 공유하기 + 주소 복사하기 + 다른 앱으로 열기 + 주소가 복사 되었습니다 + \ No newline at end of file diff --git a/library/src/main/res/values-land/dimens.xml b/library/src/main/res/values-land/dimens.xml new file mode 100644 index 0000000..df5957c --- /dev/null +++ b/library/src/main/res/values-land/dimens.xml @@ -0,0 +1,5 @@ + + + 48dp + 224dp + \ No newline at end of file diff --git a/library/src/main/res/values-ldrtl/bools.xml b/library/src/main/res/values-ldrtl/bools.xml new file mode 100644 index 0000000..42cdd7b --- /dev/null +++ b/library/src/main/res/values-ldrtl/bools.xml @@ -0,0 +1,4 @@ + + + true + \ No newline at end of file diff --git a/library/src/main/res/values-pt-rBR/strings.xml b/library/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000..4f419eb --- /dev/null +++ b/library/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,9 @@ + + + Atualizar + Procurar + Compartilhar + Copiar link + Abrir com + Copiado para a área de transferência + diff --git a/library/src/main/res/values/bools.xml b/library/src/main/res/values/bools.xml new file mode 100644 index 0000000..86ac49b --- /dev/null +++ b/library/src/main/res/values/bools.xml @@ -0,0 +1,4 @@ + + + false + \ No newline at end of file diff --git a/library/src/main/res/values/colors.xml b/library/src/main/res/values/colors.xml new file mode 100644 index 0000000..6a701fd --- /dev/null +++ b/library/src/main/res/values/colors.xml @@ -0,0 +1,20 @@ + + + #1AFFFFFF + #33FFFFFF + #4DFFFFFF + + #1AD2D2D2 + #33D2D2D2 + + #0D202020 + #1A202020 + #33202020 + #4D202020 + #66202020 + + #FFFFFF + #000000 + #A5A5A5 + #E7E7E7 + diff --git a/library/src/main/res/values/dimens.xml b/library/src/main/res/values/dimens.xml new file mode 100644 index 0000000..6dc8ffd --- /dev/null +++ b/library/src/main/res/values/dimens.xml @@ -0,0 +1,17 @@ + + + 56dp + 168dp + + 4dp + 1dp + 14dp + 10dp + 2dp + 4dp + 16dp + 16dp + 32dp + 4dp + 2dp + \ No newline at end of file diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml new file mode 100644 index 0000000..0fd3b64 --- /dev/null +++ b/library/src/main/res/values/strings.xml @@ -0,0 +1,14 @@ + + + Refresh + Find + Share via + Copy Link + Open with + Save Photo + Photo has been saved to + Photo save failed + File Chooser + Copied to clipboard + Loading video… + \ No newline at end of file diff --git a/library/src/main/res/values/styles.xml b/library/src/main/res/values/styles.xml new file mode 100644 index 0000000..c2d1d07 --- /dev/null +++ b/library/src/main/res/values/styles.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + diff --git a/library/src/main/res/xml/provider_paths.xml b/library/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000..791000e --- /dev/null +++ b/library/src/main/res/xml/provider_paths.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index b96280e..bdf9385 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,4 +17,5 @@ dependencyResolutionManagement { } rootProject.name = "LunarLauncher" -include ("app") +include ("app","library","utils") +//annotations diff --git a/utils/.gitignore b/utils/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/utils/.gitignore @@ -0,0 +1 @@ +/build diff --git a/utils/build.gradle b/utils/build.gradle new file mode 100644 index 0000000..f9da1dc --- /dev/null +++ b/utils/build.gradle @@ -0,0 +1,46 @@ +apply plugin: 'com.android.library' +//apply plugin: 'com.novoda.bintray-release' + + +android { + namespace "com.thefinestartist.helpers" + compileSdkVersion 34 +// buildToolsVersion '23.0.2' + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + + } + + lintOptions { + abortOnError false + } + + defaultConfig { + minSdkVersion 7 + targetSdkVersion 33 + versionCode 1 + versionName '0.9.5' + vectorDrawables.useSupportLibrary = true + } +} + +dependencies { + +// compile "com.thefinestartist:annotations:${rootProject.ext.versionName}" + implementation "com.android.support:appcompat-v7:28.0.0" + implementation "com.android.support:support-annotations:28.0.0" + implementation 'androidx.annotation:annotation-jvm:1.9.1' +// implementation project(':annotations') +// testCompile 'junit:junit:4.12' +} + +//publish { +// userOrg = 'thefinestartist' +// groupId = 'com.thefinestartist' +// artifactId = 'utils' +// publishVersion = rootProject.ext.versionName +// desc = 'Context free and basic utils to build Android project conveniently.' +// website = 'https://github.com/TheFinestArtist/AndroidBaseUtils' +//} diff --git a/utils/src/androidTest/java/com/thefinestartist/utils/ApplicationTest.java b/utils/src/androidTest/java/com/thefinestartist/utils/ApplicationTest.java new file mode 100644 index 0000000..89235ec --- /dev/null +++ b/utils/src/androidTest/java/com/thefinestartist/utils/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.thefinestartist.utils; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/utils/src/androidTest/java/com/thefinestartist/utils/etc/PreferencesUtilTest.java b/utils/src/androidTest/java/com/thefinestartist/utils/etc/PreferencesUtilTest.java new file mode 100755 index 0000000..9cd9921 --- /dev/null +++ b/utils/src/androidTest/java/com/thefinestartist/utils/etc/PreferencesUtilTest.java @@ -0,0 +1,278 @@ +package com.thefinestartist.utils.etc; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; + +import com.thefinestartist.Base; +import com.thefinestartist.utils.preferences.PreferencesUtil; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +/** + * Tests of the {@link PreferencesUtil} class. + * + * @author Robin Gustafsson + */ +public class PreferencesUtilTest extends AndroidTestCase { + + @Override + public void setUp() throws Exception { + super.setUp(); + Base.initialize(getContext()); + } + + @SmallTest + public void testSetGetDefaultName() { + final String expected = "TEST_DEFAULT_NAME"; + + PreferencesUtil.setDefaultName(expected); + String actual = PreferencesUtil.getDefaultName(); + assertEquals(expected, actual); + } + + @SmallTest + public void testDifferentNames() { + final String name1 = "TEST_DIFFERENTNAMES_NAME1"; + final String name2 = "TEST_DIFFERENTNAMES_NAME2"; + final String key = "TEST_DIFFERENTNAMES_KEY"; + final boolean value = true; + final boolean expected = false; + + PreferencesUtil.put(name1, key, value); + boolean actual = PreferencesUtil.get(name2, key, expected); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreBoolean() { + final String key = "TEST_BOOLEAN"; + final boolean expected = true; + final boolean defValue = false; + + PreferencesUtil.put(key, expected); + boolean actual = PreferencesUtil.get(key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreBooleanNamed() { + final String name = "TEST_NAMED"; + final String key = "TEST_BOOLEAN"; + final boolean expected = true; + final boolean defValue = false; + + PreferencesUtil.put(name, key, expected); + boolean actual = PreferencesUtil.get(name, key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreInt() { + final String key = "TEST_INT"; + final int expected = 321; + final int defValue = 0; + + PreferencesUtil.put(key, expected); + int actual = PreferencesUtil.get(key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreIntNamed() { + final String name = "TEST_NAMED"; + final String key = "TEST_INT"; + final int expected = 321; + final int defValue = 0; + + PreferencesUtil.put(name, key, expected); + int actual = PreferencesUtil.get(name, key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreFloat() { + final String key = "TEST_FLOAT"; + final float expected = 12.3f; + final float defValue = 0.0f; + + PreferencesUtil.put(key, expected); + float actual = PreferencesUtil.get(key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreFloatNamed() { + final String name = "TEST_NAMED"; + final String key = "TEST_FLOAT"; + final float expected = 12.3f; + final float defValue = 0.0f; + + PreferencesUtil.put(name, key, expected); + float actual = PreferencesUtil.get(name, key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreLong() { + final String key = "TEST_LONG"; + final long expected = 321L; + final long defValue = 0L; + + PreferencesUtil.put(key, expected); + long actual = PreferencesUtil.get(key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreLongNamed() { + final String name = "TEST_NAMED"; + final String key = "TEST_LONG"; + final long expected = 321L; + final long defValue = 0L; + + PreferencesUtil.put(name, key, expected); + long actual = PreferencesUtil.get(name, key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreString() { + final String key = "TEST_STRING"; + final String expected = "Lorem ipsum"; + final String defValue = null; + + PreferencesUtil.put(key, expected); + String actual = PreferencesUtil.get(key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreStringNamed() { + final String name = "TEST_NAMED"; + final String key = "TEST_STRING"; + final String expected = "Lorem ipsum"; + final String defValue = null; + + PreferencesUtil.put(name, key, expected); + String actual = PreferencesUtil.get(name, key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreStringSet() { + final String key = "TEST_STRINGSET"; + final Set expected = new HashSet<>(); + expected.add("Lorem ipsum"); + expected.add("dolor sit amet"); + expected.add("consectetur adipiscing elit"); + final Set defValue = null; + + PreferencesUtil.put(key, expected); + Set actual = PreferencesUtil.get(key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testStoreStringSetNamed() { + final String name = "TEST_NAMED"; + final String key = "TEST_STRINGSET"; + final Set expected = new HashSet<>(); + expected.add("Lorem ipsum"); + expected.add("dolor sit amet"); + expected.add("consectetur adipiscing elit"); + final Set defValue = null; + + PreferencesUtil.put(name, key, expected); + Set actual = PreferencesUtil.get(name, key, defValue); + assertEquals(expected, actual); + } + + @MediumTest + public void testStoreSerializable() { + final String key = "TEST_SERIALIZABLE"; + final ArrayList expected = new ArrayList<>(); + expected.add("Lorem ipsum"); + expected.add("dolor sit amet"); + expected.add("consectetur adipiscing elit"); + final ArrayList defValue = new ArrayList<>(); + defValue.add("Proin mollis dictum"); + + PreferencesUtil.put(key, expected); + ArrayList actual = PreferencesUtil.get(key, defValue); + assertEquals(expected, actual); + } + + @MediumTest + public void testStoreSerializableNamed() { + final String name = "TEST_NAMED"; + final String key = "TEST_SERIALIZABLE"; + final ArrayList expected = new ArrayList<>(); + expected.add("Lorem ipsum"); + expected.add("dolor sit amet"); + expected.add("consectetur adipiscing elit"); + final ArrayList defValue = new ArrayList<>(); + defValue.add("Proin mollis dictum"); + + PreferencesUtil.put(name, key, expected); + ArrayList actual = PreferencesUtil.get(name, key, defValue); + assertEquals(expected, actual); + } + + @SmallTest + public void testRemove() { + final String key = "TEST_REMOVE"; + final String expected = null; + + PreferencesUtil.put(key, "Lorem ipsum"); + PreferencesUtil.remove(key); + String actual = PreferencesUtil.get(key, expected); + assertEquals(expected, actual); + } + + @SmallTest + public void testRemoveNamed() { + final String name = "TEST_NAMED"; + final String key = "TEST_REMOVE"; + final String expected = null; + + PreferencesUtil.put(name, key, "Lorem ipsum"); + PreferencesUtil.remove(name, key); + String actual = PreferencesUtil.get(name, key, expected); + assertEquals(expected, actual); + } + + @SmallTest + public void testClear() { + final String[] keys = {"TEST_REMOVE_1", "TEST_REMOVE_2", "TEST_REMOVE_2"}; + final String expected = null; + + for (String key : keys) { + PreferencesUtil.put(key, "Lorem ipsum"); + } + PreferencesUtil.clear(); + for (String key : keys) { + String actual = PreferencesUtil.get(key, expected); + assertEquals(expected, actual); + } + } + + @SmallTest + public void testClearNamed() { + final String name = "TEST_NAMED"; + final String[] keys = {"TEST_REMOVE_1", "TEST_REMOVE_2", "TEST_REMOVE_2"}; + final String expected = null; + + for (String key : keys) { + PreferencesUtil.put(name, key, "Lorem ipsum"); + } + PreferencesUtil.clear(name); + for (String key : keys) { + String actual = PreferencesUtil.get(name, key, expected); + assertEquals(expected, actual); + } + } + +} diff --git a/utils/src/main/AndroidManifest.xml b/utils/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5618ce2 --- /dev/null +++ b/utils/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/Base.java b/utils/src/main/java/com/thefinestartist/Base.java new file mode 100644 index 0000000..f897b78 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/Base.java @@ -0,0 +1,57 @@ +package com.thefinestartist; + +import android.content.Context; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.util.DisplayMetrics; + +import androidx.annotation.NonNull; + +/** + * Base helps to get {@link Context}, {@link Resources}, {@link AssetManager}, {@link Configuration} and {@link DisplayMetrics} in any class. + * + * @author Leonardo Taehwan Kim + */ +public class Base { + + private static Context context; + + public static void initialize(@NonNull Context context) { + Base.context = context; + } + + public static Context getContext() { + synchronized (Base.class) { + if (Base.context == null) + throw new NullPointerException("Call Base.initialize(context) within your Application onCreate() method."); + + return Base.context.getApplicationContext(); + } + } + + public static Resources getResources() { + return Base.getContext().getResources(); + } + + public static Resources.Theme getTheme() { + return Base.getContext().getTheme(); + } + + public static AssetManager getAssets() { + return Base.getContext().getAssets(); + } + + public static Configuration getConfiguration() { + return Base.getResources().getConfiguration(); + } + + public static DisplayMetrics getDisplayMetrics() { + return Base.getResources().getDisplayMetrics(); + } +} +// TODO: Thread safety +// TODO: ripple, bitmap, time, contact list, picture list, video list, connectivity, wake lock, screen lock/off/on, get attributes, cookie, audio +// TODO: keystore +// TODO: http://jo.centis1504.net/?p=1189 +// TODO: Test codes \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/annotations/Extra.java b/utils/src/main/java/com/thefinestartist/annotations/Extra.java new file mode 100644 index 0000000..e7a510a --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/annotations/Extra.java @@ -0,0 +1,19 @@ +package com.thefinestartist.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.CLASS; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Bind a field to the bundle data for the specific key. + *

+ * {@literal @}Extra(EXTRA_TITLE) String title;
+ * 
+ */ +@Retention(CLASS) +@Target(FIELD) +public @interface Extra { + String value() default ""; +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/binders/ExtrasBinder.java b/utils/src/main/java/com/thefinestartist/binders/ExtrasBinder.java new file mode 100644 index 0000000..0ba8d77 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/binders/ExtrasBinder.java @@ -0,0 +1,56 @@ +package com.thefinestartist.binders; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +import java.lang.reflect.Method; + +/** + * ExtrasBinder binds data from {@link Intent} or {@link Bundle} to matching variable. + * + * @author Leonardo Taehwan Kim + */ +public class ExtrasBinder { + + static final String SUFFIX = "$$ExtraBinder"; + + public static void bind(Activity activity) { + if (activity == null) + return; + + bindObject(activity); + } + + public static void bind(Fragment fragment) { + if (fragment == null) + return; + + bindObject(fragment); + } + + public static void bind(android.app.Fragment fragment) { + if (fragment == null) + return; + + bindObject(fragment); + } + + private static void bindObject(@NonNull Object object) { + try { + Class binder = Class.forName(object.getClass().getName() + SUFFIX); + Method bind = binder.getMethod("bind", object.getClass()); + bind.invoke(null, object); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Unable to bind extras for " + object, e); + } + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/builders/ActivityBuilder.java b/utils/src/main/java/com/thefinestartist/builders/ActivityBuilder.java new file mode 100644 index 0000000..5aa9817 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/builders/ActivityBuilder.java @@ -0,0 +1,82 @@ +package com.thefinestartist.builders; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.thefinestartist.Base; +import com.thefinestartist.utils.content.ContextUtil; + +import java.io.Serializable; +import java.util.ArrayList; + +/** + * ActivityBuilder helps to build {@link Activity} {@link Intent} and start {@link Activity}. + * + * @author Leonardo Taehwan Kim + */ +public class ActivityBuilder { + + final Intent intent; + + public ActivityBuilder(@NonNull Class clazz) { + intent = new Intent(Base.getContext(), clazz); + } + + public ActivityBuilder set(@NonNull String key, T value) { + intent.putExtra(key, value); + return this; + } + + public ActivityBuilder set(@NonNull String key, Parcelable value) { + intent.putExtra(key, value); + return this; + } + + public ActivityBuilder set(@NonNull String key, Parcelable[] value) { + intent.putExtra(key, value); + return this; + } + + public ActivityBuilder set(@NonNull String key, ArrayList value) { + intent.putExtra(key, value); + return this; + } + + public ActivityBuilder remove(@NonNull String key) { + intent.removeExtra(key); + return this; + } + + public ActivityBuilder setFlags(int flags) { + intent.setFlags(flags); + return this; + } + + public ActivityBuilder addFlags(int flags) { + intent.addFlags(flags); + return this; + } + + public Intent buildIntent() { + return intent; + } + + public void start() { + ContextUtil.startActivity(intent); + } + + public void startForResult(@NonNull Activity activity, int requestCode) { + activity.startActivityForResult(intent, requestCode); + } + + @TargetApi(16) + public void startForResult(@NonNull Activity activity, int requestCode, @Nullable Bundle options) { + activity.startActivityForResult(intent, requestCode, options); + } +} diff --git a/utils/src/main/java/com/thefinestartist/builders/BundleBuilder.java b/utils/src/main/java/com/thefinestartist/builders/BundleBuilder.java new file mode 100644 index 0000000..fab1c70 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/builders/BundleBuilder.java @@ -0,0 +1,34 @@ +package com.thefinestartist.builders; + +import android.os.Bundle; +import android.os.Parcelable; + +import java.io.Serializable; + +/** + * BundleBuilder helps to build {@link Bundle} conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class BundleBuilder { + + final Bundle bundle = new Bundle(); + + public BundleBuilder set(String key, T value) { + bundle.putSerializable(key, value); + return this; + } + + public BundleBuilder set(String key, Parcelable value) { + bundle.putParcelable(key, value); + return this; + } + + public T get(String key) { + return (T) bundle.getSerializable(key); + } + + public Bundle build() { + return bundle; + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/converters/Unit.java b/utils/src/main/java/com/thefinestartist/converters/Unit.java new file mode 100644 index 0000000..9a1e3d6 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/converters/Unit.java @@ -0,0 +1,9 @@ +package com.thefinestartist.converters; + +/** + * Unit is abbreviation class of {@link UnitConverter}. + * + * @author Leonardo Taehwan Kim + */ +public class Unit extends UnitConverter { +} diff --git a/utils/src/main/java/com/thefinestartist/converters/UnitConverter.java b/utils/src/main/java/com/thefinestartist/converters/UnitConverter.java new file mode 100644 index 0000000..a662034 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/converters/UnitConverter.java @@ -0,0 +1,43 @@ +package com.thefinestartist.converters; + +import com.thefinestartist.Base; + +/** + * UnitConverter helps to convert dp or sp size into pixel. + * + * @author Leonardo Taehwan Kim + */ +public class UnitConverter { + + public static float dpToPx(float dp) { + return dp * Base.getDisplayMetrics().density; + } + + public static int dpToPx(int dp) { + return (int) (dp * Base.getDisplayMetrics().density + 0.5f); + } + + public static float pxToDp(float px) { + return px / Base.getDisplayMetrics().density; + } + + public static int pxToDp(int px) { + return (int) (px / Base.getDisplayMetrics().density + 0.5f); + } + + public static float spToPx(float sp) { + return sp * Base.getDisplayMetrics().scaledDensity; + } + + public static int spToPx(int sp) { + return (int) (sp * Base.getDisplayMetrics().scaledDensity + 0.5f); + } + + public static float pxToSp(float px) { + return px / Base.getDisplayMetrics().scaledDensity; + } + + public static int pxToSp(int px) { + return (int) (px / Base.getDisplayMetrics().scaledDensity + 0.5f); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/enums/LogLevel.java b/utils/src/main/java/com/thefinestartist/enums/LogLevel.java new file mode 100644 index 0000000..1482c54 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/enums/LogLevel.java @@ -0,0 +1,19 @@ +package com.thefinestartist.enums; + +import com.thefinestartist.utils.log.LogUtil; + +/** + * Enum class associated with {@link LogUtil}. + * + * @author Leonardo Taehwan Kim + */ +public enum LogLevel { + FULL, + VERBOSE, + DEBUG, + INFO, + WARN, + ERROR, + ASSERT, + NONE +} diff --git a/utils/src/main/java/com/thefinestartist/enums/Rotation.java b/utils/src/main/java/com/thefinestartist/enums/Rotation.java new file mode 100644 index 0000000..fbed605 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/enums/Rotation.java @@ -0,0 +1,29 @@ +package com.thefinestartist.enums; + +import com.thefinestartist.utils.ui.DisplayUtil; + +/** + * Enum class associated with {@link DisplayUtil}. + * + * @author Leonardo Taehwan Kim + */ +public enum Rotation { + DEGREES_0(0), + DEGREES_90(1), + DEGREES_180(2), + DEGREES_270(3); + + int value; + + Rotation(int value) { + this.value = value; + } + + public static Rotation fromValue(int value) { + for (Rotation rotation : values()) + if (rotation.value == value) + return rotation; + + return DEGREES_0; + } +} diff --git a/utils/src/main/java/com/thefinestartist/listeners/KeyboardStateListener.java b/utils/src/main/java/com/thefinestartist/listeners/KeyboardStateListener.java new file mode 100644 index 0000000..bc0cc69 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/listeners/KeyboardStateListener.java @@ -0,0 +1,14 @@ +package com.thefinestartist.listeners; + +import com.thefinestartist.utils.ui.KeyboardUtil; + +/** + * Listener class associated with {@link KeyboardUtil}. + * + * @author Leonardo Taehwan Kim + */ +public abstract class KeyboardStateListener { + + public void onStateChanged(int keyboardHeight, boolean opened) { + } +} diff --git a/utils/src/main/java/com/thefinestartist/utils/content/ContextUtil.java b/utils/src/main/java/com/thefinestartist/utils/content/ContextUtil.java new file mode 100644 index 0000000..7069e2e --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/content/ContextUtil.java @@ -0,0 +1,502 @@ +package com.thefinestartist.utils.content; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.app.WallpaperManager; +import android.content.BroadcastReceiver; +import android.content.ComponentCallbacks; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.AssetManager; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.util.AttributeSet; + +import androidx.annotation.AttrRes; +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.annotation.StyleRes; +import androidx.annotation.StyleableRes; +import androidx.core.content.ContextCompat; + +import com.thefinestartist.Base; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * ContextUtil helps to manage {@link Context} conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class ContextUtil { + + public static boolean bindService(Intent service, ServiceConnection conn, int flags) { + return Base.getContext().bindService(service, conn, flags); + } + + public static int checkCallingOrSelfPermission(String permission) { + return Base.getContext().checkCallingOrSelfPermission(permission); + } + + public static int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) { + return Base.getContext().checkCallingOrSelfUriPermission(uri, modeFlags); + } + + public static int checkCallingPermission(String permission) { + return Base.getContext().checkCallingPermission(permission); + } + + public static int checkCallingUriPermission(Uri uri, int modeFlags) { + return Base.getContext().checkCallingUriPermission(uri, modeFlags); + } + + public static int checkPermission(String permission, int pid, int uid) { + return Base.getContext().checkPermission(permission, pid, uid); + } + + public static int checkSelfPermission(@NonNull String permission) { + return ContextCompat.checkSelfPermission(Base.getContext(), permission); + } + + public static int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { + return Base.getContext().checkUriPermission(uri, pid, uid, modeFlags); + } + + public static int checkUriPermission(Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags) { + return Base.getContext().checkUriPermission(uri, readPermission, writePermission, pid, uid, modeFlags); + } + + public static Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException { + return Base.getContext().createPackageContext(packageName, flags); + } + + public static String[] databaseList() { + return Base.getContext().databaseList(); + } + + public static boolean deleteDatabase(String name) { + return Base.getContext().deleteDatabase(name); + } + + public static boolean deleteFile(String name) { + return Base.getContext().deleteFile(name); + } + + public static void enforceCallingOrSelfPermission(String permission, String message) { + Base.getContext().enforceCallingOrSelfPermission(permission, message); + } + + public static void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message) { + Base.getContext().enforceCallingOrSelfUriPermission(uri, modeFlags, message); + } + + public static void enforceCallingPermission(String permission, String message) { + Base.getContext().enforceCallingPermission(permission, message); + } + + public static void enforceCallingUriPermission(Uri uri, int modeFlags, String message) { + Base.getContext().enforceCallingUriPermission(uri, modeFlags, message); + } + + public static void enforcePermission(String permission, int pid, int uid, String message) { + Base.getContext().enforcePermission(permission, pid, uid, message); + } + + public static void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message) { + Base.getContext().enforceUriPermission(uri, pid, uid, modeFlags, message); + } + + public static void enforceUriPermission(Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags, String message) { + Base.getContext().enforceUriPermission(uri, readPermission, writePermission, pid, uid, modeFlags, message); + } + + public static String[] fileList() { + return Base.getContext().fileList(); + } + + public static Context getApplicationContext() { + return Base.getContext().getApplicationContext(); + } + + public static ApplicationInfo getApplicationInfo() { + return Base.getContext().getApplicationInfo(); + } + + public static AssetManager getAssets() { + return Base.getContext().getAssets(); + } + + public static File getCacheDir() { + return Base.getContext().getCacheDir(); + } + + public static ClassLoader getClassLoader() { + return Base.getContext().getClassLoader(); + } + + public static File getCodeCacheDir() { + return ContextCompat.getCodeCacheDir(Base.getContext()); + } + + @ColorInt + public static int getColor(@ColorRes int colorRes) { + return ContextCompat.getColor(Base.getContext(), colorRes); + } + + public static ColorStateList getColorStateList(@ColorRes int colorRes) { + return ContextCompat.getColorStateList(Base.getContext(), colorRes); + } + + public static ContentResolver getContentResolver() { + return Base.getContext().getContentResolver(); + } + + public static File getDatabasePath(String name) { + return Base.getContext().getDatabasePath(name); + } + + public static File getDir(String name, int mode) { + return Base.getContext().getDir(name, mode); + } + + public static Drawable getDrawable(@DrawableRes int drawableRes) { + return ContextCompat.getDrawable(Base.getContext(), drawableRes); + } + + @Nullable + @TargetApi(8) + public static File getExternalCacheDir() { + return Base.getContext().getExternalCacheDir(); + } + + public static File[] getExternalCacheDirs() { + return ContextCompat.getExternalCacheDirs(Base.getContext()); + } + + @Nullable + @TargetApi(8) + public static File getExternalFilesDir(String type) { + return Base.getContext().getExternalFilesDir(type); + } + + public static File[] getExternalFilesDirs(String type) { + return ContextCompat.getExternalFilesDirs(Base.getContext(), type); + } + + @TargetApi(21) + public static File[] getExternalMediaDirs() { + return Base.getContext().getExternalMediaDirs(); + } + + public static File getFileStreamPath(String name) { + return Base.getContext().getFileStreamPath(name); + } + + public static File getFilesDir() { + return Base.getContext().getFilesDir(); + } + + public static Looper getMainLooper() { + return Base.getContext().getMainLooper(); + } + + public static File getNoBackupFilesDir() { + return ContextCompat.getNoBackupFilesDir(Base.getContext()); + } + + @TargetApi(11) + public static File getObbDir() { + return Base.getContext().getObbDir(); + } + + public static File[] getObbDirs() { + return ContextCompat.getObbDirs(Base.getContext()); + } + + @TargetApi(8) + public static String getPackageCodePath() { + return Base.getContext().getPackageCodePath(); + } + + public static PackageManager getPackageManager() { + return Base.getContext().getPackageManager(); + } + + public static String getPackageName() { + return Base.getContext().getPackageName(); + } + + @TargetApi(8) + public static String getPackageResourcePath() { + return Base.getContext().getPackageResourcePath(); + } + + public static Resources getResources() { + return Base.getContext().getResources(); + } + + public static SharedPreferences getSharedPreferences(String name, int mode) { + return Base.getContext().getSharedPreferences(name, mode); + } + + public static String getString(@StringRes int stringRes) { + return Base.getContext().getString(stringRes); + } + + public static String getString(@StringRes int stringRes, Object... formatArgs) { + return Base.getContext().getString(stringRes, formatArgs); + } + + @TargetApi(23) + public static T getSystemService(Class serviceClass) { + return Base.getContext().getSystemService(serviceClass); + } + + public static Object getSystemService(String name) { + return Base.getContext().getSystemService(name); + } + + @TargetApi(23) + public static String getSystemServiceName(Class serviceClass) { + return Base.getContext().getSystemServiceName(serviceClass); + } + + public static CharSequence getText(@StringRes int stringRes) { + return Base.getContext().getText(stringRes); + } + + public static Resources.Theme getTheme() { + return Base.getContext().getTheme(); + } + + public static Drawable getWallpaper() { + return WallpaperManager.getInstance(Base.getContext()).getDrawable(); + } + + public static int getWallpaperDesiredMinimumHeight() { + return WallpaperManager.getInstance(Base.getContext()).getDesiredMinimumHeight(); + } + + public static int getWallpaperDesiredMinimumWidth() { + return WallpaperManager.getInstance(Base.getContext()).getDesiredMinimumWidth(); + } + + public static void grantUriPermission(String toPackage, Uri uri, int modeFlags) { + Base.getContext().grantUriPermission(toPackage, uri, modeFlags); + } + + public static boolean isRestricted() { + return Base.getContext().isRestricted(); + } + + public static TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { + return Base.getContext().obtainStyledAttributes(attrs); + } + + public static TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + return Base.getContext().obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes); + } + + public static TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs) { + return Base.getContext().obtainStyledAttributes(set, attrs); + } + + public static TypedArray obtainStyledAttributes(@StyleRes int resid, @StyleableRes int[] attrs) { + return Base.getContext().obtainStyledAttributes(resid, attrs); + } + + public static FileInputStream openFileInput(String name) throws FileNotFoundException { + return Base.getContext().openFileInput(name); + } + + public static FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException { + return Base.getContext().openFileOutput(name, mode); + } + + public static SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) { + return Base.getContext().openOrCreateDatabase(name, mode, factory); + } + + @TargetApi(11) + public static SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) { + return Base.getContext().openOrCreateDatabase(name, mode, factory, errorHandler); + } + + public static Drawable peekWallpaper() { + return WallpaperManager.getInstance(Base.getContext()).peekDrawable(); + } + + @TargetApi(14) + public static void registerComponentCallbacks(ComponentCallbacks callback) { + Base.getContext().registerComponentCallbacks(callback); + } + + public static Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { + return Base.getContext().registerReceiver(receiver, filter); + } + + public static Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { + return Base.getContext().registerReceiver(receiver, filter, broadcastPermission, scheduler); + } + +// public static void removeStickyBroadcast(Intent intent) { +// Base.getContext().removeStickyBroadcast(intent); +// } +// +// @TargetApi(17) +// public static void removeStickyBroadcastAsUser(Intent intent, UserHandle user) { +// Base.getContext().removeStickyBroadcastAsUser(intent, user); +// } + + public static void revokeUriPermission(Uri uri, int modeFlags) { + Base.getContext().revokeUriPermission(uri, modeFlags); + } + + public static void sendBroadcast(Intent intent, String receiverPermission) { + Base.getContext().sendBroadcast(intent, receiverPermission); + } + + public static void sendBroadcast(Intent intent) { + Base.getContext().sendBroadcast(intent); + } + + @TargetApi(17) + public static void sendBroadcastAsUser(Intent intent, UserHandle user) { + Base.getContext().sendBroadcastAsUser(intent, user); + } + + @TargetApi(17) + public static void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) { + Base.getContext().sendBroadcastAsUser(intent, user, receiverPermission); + } + + public static void sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + Base.getContext().sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras); + } + + public static void sendOrderedBroadcast(Intent intent, String receiverPermission) { + Base.getContext().sendOrderedBroadcast(intent, receiverPermission); + } + + @TargetApi(17) + public static void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + Base.getContext().sendOrderedBroadcastAsUser(intent, user, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras); + } + +// public static void sendStickyBroadcast(Intent intent) { +// Base.getContext().sendStickyBroadcast(intent); +// } +// +// @TargetApi(17) +// public static void sendStickyBroadcastAsUser(Intent intent, UserHandle user) { +// Base.getContext().sendStickyBroadcastAsUser(intent, user); +// } +// +// public static void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { +// Base.getContext().sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode, initialData, initialExtras); +// } +// +// @TargetApi(17) +// public static void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { +// Base.getContext().sendStickyOrderedBroadcastAsUser(intent, user, resultReceiver, scheduler, initialCode, initialData, initialExtras); +// } + + public static void setTheme(@StyleRes int styleRes) { + Base.getContext().setTheme(styleRes); + } + + @SuppressLint("MissingPermission") + public static void setWallpaper(InputStream data) throws IOException { + WallpaperManager.getInstance(Base.getContext()).setStream(data); + } + + @SuppressLint("MissingPermission") + public static void setWallpaper(Bitmap bitmap) throws IOException { + WallpaperManager.getInstance(Base.getContext()).setBitmap(bitmap); + } + + public static boolean startActivities(Intent[] intents, Bundle options) { + for (Intent intent : intents) + if (intent != null) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return ContextCompat.startActivities(Base.getContext(), intents, options); + } + + public static boolean startActivities(Intent[] intents) { + for (Intent intent : intents) + if (intent != null) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return ContextCompat.startActivities(Base.getContext(), intents); + } + + public static void startActivity(@NonNull Intent intent) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Base.getContext().startActivity(intent); + } + + @TargetApi(16) + public static void startActivity(Intent intent, Bundle options) { + Base.getContext().startActivity(intent, options); + } + + public static boolean startInstrumentation(ComponentName className, String profileFile, Bundle arguments) { + return Base.getContext().startInstrumentation(className, profileFile, arguments); + } + + @TargetApi(16) + public static void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) throws IntentSender.SendIntentException { + Base.getContext().startIntentSender(intent, fillInIntent, flagsMask, flagsValues, extraFlags, options); + } + + public static void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws IntentSender.SendIntentException { + Base.getContext().startIntentSender(intent, fillInIntent, flagsMask, flagsValues, extraFlags); + } + + public static ComponentName startService(Intent service) { + return Base.getContext().startService(service); + } + + public static boolean stopService(Intent service) { + return Base.getContext().stopService(service); + } + + public static void unbindService(ServiceConnection conn) { + Base.getContext().unbindService(conn); + } + + @TargetApi(14) + public static void unregisterComponentCallbacks(ComponentCallbacks callback) { + Base.getContext().unregisterComponentCallbacks(callback); + } + + public static void unregisterReceiver(BroadcastReceiver receiver) { + Base.getContext().unregisterReceiver(receiver); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/content/Ctx.java b/utils/src/main/java/com/thefinestartist/utils/content/Ctx.java new file mode 100644 index 0000000..5db6c95 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/content/Ctx.java @@ -0,0 +1,9 @@ +package com.thefinestartist.utils.content; + +/** + * Ctx is abbreviation class of {@link ContextUtil}. + * + * @author Leonardo Taehwan Kim + */ +public class Ctx extends ContextUtil { +} diff --git a/utils/src/main/java/com/thefinestartist/utils/content/Res.java b/utils/src/main/java/com/thefinestartist/utils/content/Res.java new file mode 100644 index 0000000..a4492cb --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/content/Res.java @@ -0,0 +1,9 @@ +package com.thefinestartist.utils.content; + +/** + * Res is abbreviation class of {@link ResourcesUtil}. + * + * @author Leonardo Taehwan Kim + */ +public class Res extends ResourcesUtil { +} diff --git a/utils/src/main/java/com/thefinestartist/utils/content/ResourcesUtil.java b/utils/src/main/java/com/thefinestartist/utils/content/ResourcesUtil.java new file mode 100644 index 0000000..de61235 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/content/ResourcesUtil.java @@ -0,0 +1,278 @@ +package com.thefinestartist.utils.content; + +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.Movie; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; + +import androidx.annotation.AnimRes; +import androidx.annotation.AnyRes; +import androidx.annotation.ArrayRes; +import androidx.annotation.BoolRes; +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.DimenRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.IntegerRes; +import androidx.annotation.LayoutRes; +import androidx.annotation.PluralsRes; +import androidx.annotation.RawRes; +import androidx.annotation.StringRes; +import androidx.annotation.XmlRes; + +import com.thefinestartist.Base; +import com.thefinestartist.utils.etc.APILevel; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; + +/** + * ResourcesUtil helps to manage {@link Resources} conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class ResourcesUtil { + + private static void finishPreloading() { + Base.getResources().finishPreloading(); + } + + private static void flushLayoutCache() { + Base.getResources().flushLayoutCache(); + } + + public static XmlResourceParser getAnimation(@AnimRes int animRes) { + return Base.getResources().getAnimation(animRes); + } + + public static AssetManager getAssets() { + return Base.getResources().getAssets(); + } + + public static boolean getBoolean(@BoolRes int boolRes) { + return Base.getResources().getBoolean(boolRes); + } + + @ColorInt + public static int getColor(@ColorRes int colorRes) { + return ContextUtil.getColor(colorRes); + } + + @ColorInt + public static int getColor(@ColorRes int colorRes, Resources.Theme theme) { + if (APILevel.require(23)) + return Base.getResources().getColor(colorRes, theme); + else + return getColor(colorRes); + } + + public static ColorStateList getColorStateList(@ColorRes int colorRes) { + return ContextUtil.getColorStateList(colorRes); + } + + public static ColorStateList getColorStateList(@ColorRes int colorRes, Resources.Theme theme) { + if (APILevel.require(23)) + return Base.getResources().getColorStateList(colorRes, theme); + else + return getColorStateList(colorRes); + } + + public static Configuration getConfiguration() { + return Base.getConfiguration(); + } + + public static float getDimension(@DimenRes int dimenRes) { + return Base.getResources().getDimension(dimenRes); + } + + public static int getDimensionPixelOffset(@DimenRes int dimenRes) { + return Base.getResources().getDimensionPixelOffset(dimenRes); + } + + public static int getDimensionPixelSize(@DimenRes int dimenRes) { + return Base.getResources().getDimensionPixelSize(dimenRes); + } + + public static DisplayMetrics getDisplayMetrics() { + return Base.getDisplayMetrics(); + } + + public static Drawable getDrawable(@DrawableRes int drawableRes) { + return ContextUtil.getDrawable(drawableRes); + } + + public static Drawable getDrawable(@DrawableRes int drawableRes, Resources.Theme theme) { + if (APILevel.require(21)) + return Base.getResources().getDrawable(drawableRes, theme); + else + return Base.getResources().getDrawable(drawableRes); + } + + public static Drawable getDrawableForDensity(@DrawableRes int drawableRes, int density) { + if (APILevel.require(21)) + return Base.getResources().getDrawableForDensity(drawableRes, density, Base.getContext().getTheme()); + else if (APILevel.require(15)) + return Base.getResources().getDrawableForDensity(drawableRes, density); + else + return Base.getResources().getDrawable(drawableRes); + } + + public static float getFraction(int id, int base, int pbase) { + return Base.getResources().getFraction(id, base, pbase); + } + + public static int getIdentifier(String name, String defType, String defPackage) { + return Base.getResources().getIdentifier(name, defType, defPackage); + } + + public static int[] getIntArray(@ArrayRes int arrayRes) { + return Base.getResources().getIntArray(arrayRes); + } + + public static int getInteger(@IntegerRes int integerRes) { + return Base.getResources().getInteger(integerRes); + } + + public static XmlResourceParser getLayout(@LayoutRes int layoutRes) { + return Base.getResources().getLayout(layoutRes); + } + + public static Movie getMovie(@RawRes int rawRes) { + return Base.getResources().getMovie(rawRes); + } + + public static String getQuantityString(int id, int quantity, Object... formatArgs) { + return Base.getResources().getQuantityString(id, quantity, formatArgs); + } + + public static String getQuantityString(@PluralsRes int pluralsRes, int quantity) throws Resources.NotFoundException { + return Base.getResources().getQuantityString(pluralsRes, quantity); + } + + public static CharSequence getQuantityText(int id, int quantity) { + return Base.getResources().getQuantityText(id, quantity); + } + + public static String getResourceEntryName(@AnyRes int anyRes) { + return Base.getResources().getResourceEntryName(anyRes); + } + + public static String getResourceName(@AnyRes int anyRes) { + return Base.getResources().getResourceName(anyRes); + } + + public static String getResourcePackageName(@AnyRes int anyRes) { + return Base.getResources().getResourcePackageName(anyRes); + } + + public static String getResourceTypeName(@AnyRes int anyRes) { + return Base.getResources().getResourceTypeName(anyRes); + } + + public static String getString(@StringRes int stringRes) { + return Base.getResources().getString(stringRes); + } + + public static String getString(@StringRes int stringRes, Object... formatArgs) { + return Base.getResources().getString(stringRes, formatArgs); + } + + public static String[] getStringArray(@ArrayRes int arrayRes) { + return Base.getResources().getStringArray(arrayRes); + } + + public static Resources getSystem() { + return Base.getResources().getSystem(); + } + + public static CharSequence getText(@StringRes int stringRes, CharSequence def) { + return Base.getResources().getText(stringRes, def); + } + + public static CharSequence getText(@StringRes int stringRes) { + return Base.getResources().getText(stringRes); + } + + public static CharSequence[] getTextArray(@ArrayRes int arrayRes) { + return Base.getResources().getTextArray(arrayRes); + } + + public static void getValue(String name, TypedValue outValue, boolean resolveRefs) { + Base.getResources().getValue(name, outValue, resolveRefs); + } + + public static void getValue(@AnyRes int anyRes, TypedValue outValue, boolean resolveRefs) { + Base.getResources().getValue(anyRes, outValue, resolveRefs); + } + + public static void getValueForDensity(@AnyRes int anyRes, int density, TypedValue outValue, boolean resolveRefs) { + if (APILevel.require(15)) + Base.getResources().getValueForDensity(anyRes, density, outValue, resolveRefs); + else + Base.getResources().getValue(anyRes, outValue, resolveRefs); + } + + public static XmlResourceParser getXml(@XmlRes int xmlRes) { + return Base.getResources().getXml(xmlRes); + } + + public static Resources.Theme newTheme() { + return Base.getResources().newTheme(); + } + + public static TypedArray obtainAttributes(AttributeSet set, int[] attrs) { + return Base.getResources().obtainAttributes(set, attrs); + } + + public static TypedArray obtainTypedArray(@ArrayRes int anyRes) { + return Base.getResources().obtainTypedArray(anyRes); + } + + public static InputStream openRawResource(@RawRes int rawRes) { + return Base.getResources().openRawResource(rawRes); + } + + public static InputStream openRawResource(@RawRes int rawRes, TypedValue value) { + return Base.getResources().openRawResource(rawRes, value); + } + + public static AssetFileDescriptor openRawResourceFd(@RawRes int rawRes) { + return Base.getResources().openRawResourceFd(rawRes); + } + + public static void parseBundleExtra(String tagName, AttributeSet attrs, Bundle outBundle) throws XmlPullParserException { + Base.getResources().parseBundleExtra(tagName, attrs, outBundle); + } + + public static void parseBundleExtras(XmlResourceParser parser, Bundle outBundle) throws XmlPullParserException, IOException { + Base.getResources().parseBundleExtras(parser, outBundle); + } + + public static void updateConfiguration(Configuration config, DisplayMetrics metrics) { + Base.getResources().updateConfiguration(config, metrics); + } + + // Added methods + public static int[] getColorArray(@ArrayRes int array) { + if (array == 0) + return null; + + TypedArray typedArray = Base.getResources().obtainTypedArray(array); + int[] colors = new int[typedArray.length()]; + for (int i = 0; i < typedArray.length(); i++) + colors[i] = typedArray.getColor(i, 0); + typedArray.recycle(); + return colors; + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/content/ThemeUtil.java b/utils/src/main/java/com/thefinestartist/utils/content/ThemeUtil.java new file mode 100644 index 0000000..c239301 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/content/ThemeUtil.java @@ -0,0 +1,64 @@ +package com.thefinestartist.utils.content; + +import android.annotation.TargetApi; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.TypedValue; + +import androidx.annotation.AttrRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.StyleRes; +import androidx.annotation.StyleableRes; + +import com.thefinestartist.Base; + +/** + * ThemeUtil helps to manage {@link Resources.Theme} conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class ThemeUtil { + + public static void applyStyle(int resId, boolean force) { + Base.getTheme().applyStyle(resId, force); + } + + public static void dump(int priority, String tag, String prefix) { + Base.getTheme().dump(priority, tag, prefix); + } + + @TargetApi(23) + public static int getChangingConfigurations() { + return Base.getTheme().getChangingConfigurations(); + } + + public static Drawable getDrawable(@DrawableRes int drawableRes) { + return ResourcesUtil.getDrawable(drawableRes); + } + + public static Resources getResources() { + return Base.getResources(); + } + + public static TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { + return Base.getTheme().obtainStyledAttributes(attrs); + } + + public static TypedArray obtainStyledAttributes(@StyleRes int resid, @StyleableRes int[] attrs) { + return Base.getTheme().obtainStyledAttributes(resid, attrs); + } + + public static TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + return Base.getTheme().obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes); + } + + public static boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) { + return Base.getTheme().resolveAttribute(resid, outValue, resolveRefs); + } + + public static void setTo(Resources.Theme other) { + Base.getTheme().setTo(other); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/content/TypedValueUtil.java b/utils/src/main/java/com/thefinestartist/utils/content/TypedValueUtil.java new file mode 100644 index 0000000..1a22773 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/content/TypedValueUtil.java @@ -0,0 +1,29 @@ +package com.thefinestartist.utils.content; + +import android.util.TypedValue; + +import com.thefinestartist.Base; + +/** + * TypedValueUtil helps to manage {@link TypedValue} class conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class TypedValueUtil { + + public static float applyDimension(int unit, float value) { + return TypedValue.applyDimension(unit, value, Base.getDisplayMetrics()); + } + + public static float complexToDimension(int data) { + return TypedValue.complexToDimension(data, Base.getDisplayMetrics()); + } + + public static int complexToDimensionPixelOffset(int data) { + return TypedValue.complexToDimensionPixelOffset(data, Base.getDisplayMetrics()); + } + + public static int complexToDimensionPixelSize(int data) { + return TypedValue.complexToDimensionPixelSize(data, Base.getDisplayMetrics()); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/etc/APILevel.java b/utils/src/main/java/com/thefinestartist/utils/etc/APILevel.java new file mode 100644 index 0000000..0c872fb --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/etc/APILevel.java @@ -0,0 +1,237 @@ +package com.thefinestartist.utils.etc; + +import android.os.Build; + +/** + * APILevel helps to check device API {@link android.os.Build.VERSION} conveniently. + * + * @author Marcos Trujillo, Leonardo Taehwan Kim + */ +public class APILevel { + + /** + * @param level minimum API level version that has to support the device + * @return true when the caller API version is at least level + */ + public static boolean require(int level) { + return Build.VERSION.SDK_INT >= level; + } + + /** + * @return true when the caller API version is at least Cupcake 3 + */ + public static boolean requireCupcake() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE; + } + + /** + * @return true when the caller API version is at least Donut 4 + */ + public static boolean requireDonut() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.DONUT; + } + + /** + * @return true when the caller API version is at least Eclair 5 + */ + public static boolean requireEclair() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR; + } + + /** + * @return true when the caller API version is at least Froyo 8 + */ + public static boolean requireFroyo() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO; + } + + /** + * @return true when the caller API version is at least GingerBread 9 + */ + public static boolean requireGingerbread() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; + } + + /** + * @return true when the caller API version is at least Honeycomb 11 + */ + public static boolean requireHoneycomb() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; + } + + /** + * @return true when the caller API version is at least Honeycomb 3.2, 13 + */ + public static boolean requireHoneycombMR2() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2; + } + + /** + * @return true when the caller API version is at least ICS 14 + */ + public static boolean requireIceCreamSandwich() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; + } + + /** + * @return true when the caller API version is at least JellyBean 16 + */ + public static boolean requireJellyBean() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; + } + + /** + * @return true when the caller API version is at least JellyBean MR1 17 + */ + public static boolean requireJellyBeanMR1() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1; + } + + /** + * @return true when the caller API version is at least JellyBean MR2 18 + */ + public static boolean requireJellyBeanMR2() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2; + } + + /** + * @return true when the caller API version is at least Kitkat 19 + */ + public static boolean requireKitkat() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + } + + /** + * @return true when the caller API version is at least Lollipop 21 + */ + public static boolean requireLollipop() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + } + + /** + * @return true when the caller API version is at least Lollipop MR1 22 + */ + public static boolean requireLollipopMR1() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1; + } + + /** + * @return true when the caller API version is at least Marshmallow 23 + */ + public static boolean requireMarshmallow() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; + } + + /** + * @param level API level version that specific method or variable has been deprecated + * @return true when the caller API version is less than level + */ + public static boolean deprecatedAt(int level) { + return Build.VERSION.SDK_INT < level; + } + + /** + * @return true when the caller API version is less than Cupcake 3 + */ + public static boolean deprecatedAtCupcake() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.CUPCAKE; + } + + /** + * @return true when the caller API version is less than Donut 4 + */ + public static boolean deprecatedAtDonut() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.DONUT; + } + + /** + * @return true when the caller API version is less than Eclair 5 + */ + public static boolean deprecatedAtEclair() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR; + } + + /** + * @return true when the caller API version is less than Froyo 8 + */ + public static boolean deprecatedAtFroyo() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO; + } + + /** + * @return true when the caller API version is less than GingerBread 9 + */ + public static boolean deprecatedAtGingerbread() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD; + } + + /** + * @return true when the caller API version is less than Honeycomb 11 + */ + public static boolean deprecatedAtHoneycomb() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB; + } + + /** + * @return true when the caller API version is less than Honeycomb 3.2, 13 + */ + public static boolean deprecatedAtHoneycombMR2() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2; + } + + /** + * @return true when the caller API version is less than ICS 14 + */ + public static boolean deprecatedAtIceCreamSandwich() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH; + } + + /** + * @return true when the caller API version is less than JellyBean 16 + */ + public static boolean deprecatedAtJellyBean() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN; + } + + /** + * @return true when the caller API version is less than JellyBean MR1 17 + */ + public static boolean deprecatedAtJellyBeanMR1() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1; + } + + /** + * @return true when the caller API version is less than JellyBean MR2 18 + */ + public static boolean deprecatedAtJellyBeanMR2() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2; + } + + /** + * @return true when the caller API version is less than Kitkat 19 + */ + public static boolean deprecatedAtKitkat() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT; + } + + /** + * @return true when the caller API version is less than Lollipop 21 + */ + public static boolean deprecatedAtLollipop() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; + } + + /** + * @return true when the caller API version is less than Lollipop MR1 22 + */ + public static boolean deprecatedAtLollipopMR1() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1; + } + + /** + * @return true when the caller API version is less than Marshmallow 23 + */ + public static boolean deprecatedAtMarshmallow() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.M; + } +} diff --git a/utils/src/main/java/com/thefinestartist/utils/etc/IntArrayUtil.java b/utils/src/main/java/com/thefinestartist/utils/etc/IntArrayUtil.java new file mode 100755 index 0000000..3a5feff --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/etc/IntArrayUtil.java @@ -0,0 +1,29 @@ +package com.thefinestartist.utils.etc; + +/** + * IntArrayUtil helps to manage IntArray conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class IntArrayUtil { + + public static boolean contains(int[] array, int value) { + if (array == null) + return false; + + for (int i : array) + if (i == value) + return true; + return false; + } + + public static int[] add(int[] array, int value) { + if (array == null) + return new int[]{value}; + + int[] newArray = new int[array.length + 1]; + System.arraycopy(array, 0, newArray, 0, array.length); + newArray[array.length] = value; + return newArray; + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/etc/PackageUtil.java b/utils/src/main/java/com/thefinestartist/utils/etc/PackageUtil.java new file mode 100644 index 0000000..7ff8539 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/etc/PackageUtil.java @@ -0,0 +1,58 @@ +package com.thefinestartist.utils.etc; + +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; + +import com.thefinestartist.Base; + +/** + * PackageUtil helps to handle methods related to package. + * + * @author Leonardo Taehwan Kim + */ +public class PackageUtil { + + public static final String FACEBOOK = "com.facebook.katana"; + public static final String TWITTER = "com.twitter.android"; + public static final String GOOGLE_PLUS = "com.google.android.apps.plus"; + public static final String GMAIL = "com.google.android.gm"; + public static final String PINTEREST = "com.pinterest"; + public static final String TUMBLR = "com.tumblr"; + public static final String FANCY = "com.thefancy.app"; + public static final String FLIPBOARD = "flipboard.app"; + public static final String KAKAOTALK = "com.kakao.talk"; + public static final String KAKAOSTORY = "com.kakao.story"; + + public static boolean isInstalled(String packageName) { + PackageManager packageManager = Base.getContext().getPackageManager(); + try { + packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); + return true; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + public static String getPackageName() { + return Base.getContext().getPackageName(); + } + + public static void openPlayStore() { + String packageName = Base.getContext().getPackageName(); + openPlayStore(packageName); + } + + public static void openPlayStore(String packageName) { + try { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + packageName)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Base.getContext().startActivity(intent); + } catch (ActivityNotFoundException exception) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=" + packageName)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Base.getContext().startActivity(intent); + } + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/etc/SparseArrayUtil.java b/utils/src/main/java/com/thefinestartist/utils/etc/SparseArrayUtil.java new file mode 100644 index 0000000..66468a6 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/etc/SparseArrayUtil.java @@ -0,0 +1,24 @@ +package com.thefinestartist.utils.etc; + +import android.util.SparseArray; + +import java.util.ArrayList; +import java.util.List; + +/** + * SparseArrayUtil helps to manage SparseArray conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class SparseArrayUtil { + + public static List asArrayList(SparseArray sparseArray) { + if (sparseArray == null) + return new ArrayList(); + + ArrayList arrayList = new ArrayList(sparseArray.size()); + for (int i = 0; i < sparseArray.size(); i++) + arrayList.add(sparseArray.valueAt(i)); + return arrayList; + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/etc/ThreadUtil.java b/utils/src/main/java/com/thefinestartist/utils/etc/ThreadUtil.java new file mode 100644 index 0000000..6bce5ae --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/etc/ThreadUtil.java @@ -0,0 +1,15 @@ +package com.thefinestartist.utils.etc; + +import android.os.Looper; + +/** + * ThreadUtil helps to manage thread conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class ThreadUtil { + + public static boolean isMain() { + return Looper.myLooper() == Looper.getMainLooper(); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/etc/TypefaceUtil.java b/utils/src/main/java/com/thefinestartist/utils/etc/TypefaceUtil.java new file mode 100644 index 0000000..32e600f --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/etc/TypefaceUtil.java @@ -0,0 +1,55 @@ +package com.thefinestartist.utils.etc; + +import android.graphics.Typeface; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.collection.SimpleArrayMap; + +import com.thefinestartist.Base; + +/** + * TypefaceUtil helps to retrieve typeface from assets folder. + * + * @author Leonardo Taehwan Kim + */ +public class TypefaceUtil { + + private static final SimpleArrayMap cache = new SimpleArrayMap<>(); + + public static Typeface get(@NonNull String path) { + synchronized (cache) { + if (cache.containsKey(path)) + return cache.get(path); + + try { + Typeface typeface = Typeface.createFromAsset(Base.getContext().getAssets(), path); + cache.put(path, typeface); + return typeface; + } catch (RuntimeException e) { + return null; + } + } + } + + public static void setTypeface(@NonNull String path, TextView... textViews) { + if (textViews == null) + return; + + for (TextView textView : textViews) + if (textView != null) + textView.setTypeface(get(path)); + } + + public static void setTypeface(@NonNull String path, boolean includeFontPadding, TextView... textViews) { + if (textViews == null) + return; + + for (TextView textView : textViews) { + if (textView != null) { + textView.setTypeface(get(path)); + textView.setIncludeFontPadding(includeFontPadding); + } + } + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/log/AndroidLogPrinter.java b/utils/src/main/java/com/thefinestartist/utils/log/AndroidLogPrinter.java new file mode 100644 index 0000000..1725169 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/log/AndroidLogPrinter.java @@ -0,0 +1,9 @@ +package com.thefinestartist.utils.log; + +/** + * AndroidLogPrinter log message via Android system. + * + * @author Leonardo Taehwan Kim + */ +public class AndroidLogPrinter extends LogPrinter { +} diff --git a/utils/src/main/java/com/thefinestartist/utils/log/FileLogPrinter.java b/utils/src/main/java/com/thefinestartist/utils/log/FileLogPrinter.java new file mode 100644 index 0000000..9c6b194 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/log/FileLogPrinter.java @@ -0,0 +1,40 @@ +package com.thefinestartist.utils.log; + +/** + * FileLogPrinter log message via file system. + * + * @author Leonardo Taehwan Kim + */ +public class FileLogPrinter extends LogPrinter { + + @Override + public void v(String tag, String message) { + super.v(tag, message); + } + + @Override + public void d(String tag, String message) { + super.d(tag, message); + } + + @Override + public void i(String tag, String message) { + super.i(tag, message); + } + + @Override + public void w(String tag, String message) { + super.w(tag, message); + } + + @Override + public void e(String tag, String message) { + super.e(tag, message); + } + + @Override + public void wtf(String tag, String message) { + super.wtf(tag, message); + } +} +// TODO: Finish this file after FileUtil \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/log/L.java b/utils/src/main/java/com/thefinestartist/utils/log/L.java new file mode 100644 index 0000000..709edcd --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/log/L.java @@ -0,0 +1,9 @@ +package com.thefinestartist.utils.log; + +/** + * L is abbreviation class of {@link LogUtil}. + * + * @author Leonardo Taehwan Kim + */ +public class L extends LogUtil { +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/log/LogHelper.java b/utils/src/main/java/com/thefinestartist/utils/log/LogHelper.java new file mode 100644 index 0000000..1db79eb --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/log/LogHelper.java @@ -0,0 +1,702 @@ +package com.thefinestartist.utils.log; + +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.StringRes; + +import com.thefinestartist.enums.LogLevel; +import com.thefinestartist.utils.content.ResourcesUtil; +import com.thefinestartist.utils.etc.APILevel; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Arrays; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * LogHelper helps to deal with {@link Log} conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class LogHelper { + + private static final int INDENT_SPACES = 4; + // http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%E2%94%80-%E2%95%BF%EF%BF%A8%5D + private static final String TOP_DIVIDER = "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"; + private static final String MIDDLE_DIVIDER = "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"; + private static final String BOTTOM_DIVIDER = "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"; + + protected Settings settings = new Settings(LogHelper.class.getSimpleName()); + + // Constructors + public LogHelper() { + } + + public LogHelper(String tag) { + settings.setTag(tag); + } + + public LogHelper(@StringRes int tagRes) { + settings.setTag(ResourcesUtil.getString(tagRes)); + } + + public LogHelper(Class clazz) { + settings.setTag(clazz.getSimpleName()); + } + + // Setters + public LogHelper tag(String tag) { + settings.setTag(tag); + return this; + } + + public LogHelper tag(@StringRes int tagRes) { + settings.setTag(tagRes); + return this; + } + + public LogHelper tag(Class clazz) { + settings.setTag(clazz); + return this; + } + + public LogHelper showThreadInfo(boolean showThreadInfo) { + settings.setShowThreadInfo(showThreadInfo); + return this; + } + + public LogHelper stackTraceCount(int stackTraceCount) { + settings.setStackTraceCount(stackTraceCount); + return this; + } + + public LogHelper logLevel(LogLevel logLevel) { + settings.setLogLevel(logLevel); + return this; + } + + public LogHelper showDivider(boolean showDivider) { + settings.setShowDivider(showDivider); + return this; + } + + public LogHelper logPrinter(LogPrinter logPrinter) { + settings.setLogPrinter(logPrinter); + return this; + } + + // Logging Verbose + public void v(byte message) { + log(LogLevel.VERBOSE, message); + } + + public void v(char message) { + log(LogLevel.VERBOSE, message); + } + + public void v(short message) { + log(LogLevel.VERBOSE, message); + } + + public void v(int message) { + log(LogLevel.VERBOSE, message); + } + + public void v(long message) { + log(LogLevel.VERBOSE, message); + } + + public void v(float message) { + log(LogLevel.VERBOSE, message); + } + + public void v(double message) { + log(LogLevel.VERBOSE, message); + } + + public void v(boolean message) { + log(LogLevel.VERBOSE, message); + } + + public void v(String message) { + log(LogLevel.VERBOSE, message); + } + + public void v(JSONObject message) { + log(LogLevel.VERBOSE, message); + } + + public void v(JSONArray message) { + log(LogLevel.VERBOSE, message); + } + + public void v(Exception message) { + log(LogLevel.VERBOSE, message); + } + + public void v(Object message) { + log(LogLevel.VERBOSE, message); + } + + // Logging Debug + public void d(byte message) { + log(LogLevel.DEBUG, message); + } + + public void d(char message) { + log(LogLevel.DEBUG, message); + } + + public void d(short message) { + log(LogLevel.DEBUG, message); + } + + public void d(int message) { + log(LogLevel.DEBUG, message); + } + + public void d(long message) { + log(LogLevel.DEBUG, message); + } + + public void d(float message) { + log(LogLevel.DEBUG, message); + } + + public void d(double message) { + log(LogLevel.DEBUG, message); + } + + public void d(boolean message) { + log(LogLevel.DEBUG, message); + } + + public void d(String message) { + log(LogLevel.DEBUG, message); + } + + public void d(JSONObject message) { + log(LogLevel.DEBUG, message); + } + + public void d(JSONArray message) { + log(LogLevel.DEBUG, message); + } + + public void d(Exception message) { + log(LogLevel.DEBUG, message); + } + + public void d(Object message) { + log(LogLevel.DEBUG, message); + } + + // Logging Information + public void i(byte message) { + log(LogLevel.INFO, message); + } + + public void i(char message) { + log(LogLevel.INFO, message); + } + + public void i(short message) { + log(LogLevel.INFO, message); + } + + public void i(int message) { + log(LogLevel.INFO, message); + } + + public void i(long message) { + log(LogLevel.INFO, message); + } + + public void i(float message) { + log(LogLevel.INFO, message); + } + + public void i(double message) { + log(LogLevel.INFO, message); + } + + public void i(boolean message) { + log(LogLevel.INFO, message); + } + + public void i(String message) { + log(LogLevel.INFO, message); + } + + public void i(JSONObject message) { + log(LogLevel.INFO, message); + } + + public void i(JSONArray message) { + log(LogLevel.INFO, message); + } + + public void i(Exception message) { + log(LogLevel.INFO, message); + } + + public void i(Object message) { + log(LogLevel.INFO, message); + } + + // Logging Warning + public void w(byte message) { + log(LogLevel.WARN, message); + } + + public void w(char message) { + log(LogLevel.WARN, message); + } + + public void w(short message) { + log(LogLevel.WARN, message); + } + + public void w(int message) { + log(LogLevel.WARN, message); + } + + public void w(long message) { + log(LogLevel.WARN, message); + } + + public void w(float message) { + log(LogLevel.WARN, message); + } + + public void w(double message) { + log(LogLevel.WARN, message); + } + + public void w(boolean message) { + log(LogLevel.WARN, message); + } + + public void w(String message) { + log(LogLevel.WARN, message); + } + + public void w(JSONObject message) { + log(LogLevel.WARN, message); + } + + public void w(JSONArray message) { + log(LogLevel.WARN, message); + } + + public void w(Exception message) { + log(LogLevel.WARN, message); + } + + public void w(Object message) { + log(LogLevel.WARN, message); + } + + // Logging Error + public void e(byte message) { + log(LogLevel.ERROR, message); + } + + public void e(char message) { + log(LogLevel.ERROR, message); + } + + public void e(short message) { + log(LogLevel.ERROR, message); + } + + public void e(int message) { + log(LogLevel.ERROR, message); + } + + public void e(long message) { + log(LogLevel.ERROR, message); + } + + public void e(float message) { + log(LogLevel.ERROR, message); + } + + public void e(double message) { + log(LogLevel.ERROR, message); + } + + public void e(boolean message) { + log(LogLevel.ERROR, message); + } + + public void e(String message) { + log(LogLevel.ERROR, message); + } + + public void e(JSONObject message) { + log(LogLevel.ERROR, message); + } + + public void e(JSONArray message) { + log(LogLevel.ERROR, message); + } + + public void e(Exception message) { + log(LogLevel.ERROR, message); + } + + public void e(Object message) { + log(LogLevel.ERROR, message); + } + + // Logging Assert + public void wtf(byte message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(char message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(short message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(int message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(long message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(float message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(double message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(boolean message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(String message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(JSONObject message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(JSONArray message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(Exception message) { + log(LogLevel.ASSERT, message); + } + + public void wtf(Object message) { + log(LogLevel.ASSERT, message); + } + + // Logging JsonString + public void json(String jsonString) { + json(LogLevel.DEBUG, jsonString); + } + + public void json(LogLevel logLevel, String jsonString) { + if (TextUtils.isEmpty(jsonString)) { + log(logLevel, "Json string is empty."); + } else { + jsonString = jsonString.trim(); + + try { + if (jsonString.startsWith("{")) { + JSONObject jsonObject = new JSONObject(jsonString); + String message = jsonObject.toString(INDENT_SPACES); + log(logLevel, message); + return; + } + if (jsonString.startsWith("[")) { + JSONArray jsonArray = new JSONArray(jsonString); + String message = jsonArray.toString(INDENT_SPACES); + log(logLevel, message); + } + } catch (JSONException e) { + log(logLevel, e); + } + } + } + + // Logging XmlString + public void xml(String xmlString) { + xml(LogLevel.DEBUG, xmlString); + } + + public void xml(LogLevel logLevel, String xmlString) { + if (TextUtils.isEmpty(xmlString)) { + log(logLevel, "Xml string is empty."); + } else { + if (APILevel.require(8)) { + try { + Source xmlInput = new StreamSource(new StringReader(xmlString)); + StreamResult xmlOutput = new StreamResult(new StringWriter()); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.transform(xmlInput, xmlOutput); + log(logLevel, xmlOutput.getWriter().toString().replaceFirst(">", ">\n")); + } catch (TransformerException e) { + log(logLevel, e); + } + } else { + log(logLevel, xmlString); + } + } + } + + // Printing + private void log(LogLevel logLevel, byte message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + printString(logLevel, String.valueOf(message)); + } + + private void log(LogLevel logLevel, char message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + printString(logLevel, String.valueOf(message)); + } + + private void log(LogLevel logLevel, short message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + printString(logLevel, String.valueOf(message)); + } + + private void log(LogLevel logLevel, int message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + printString(logLevel, String.valueOf(message)); + } + + private void log(LogLevel logLevel, long message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + printString(logLevel, String.valueOf(message)); + } + + private void log(LogLevel logLevel, float message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + printString(logLevel, String.valueOf(message)); + } + + private void log(LogLevel logLevel, double message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + printString(logLevel, String.valueOf(message)); + } + + private void log(LogLevel logLevel, boolean message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + printString(logLevel, String.valueOf(message)); + } + + private void log(LogLevel logLevel, String message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + printString(logLevel, message); + } + + private void log(LogLevel logLevel, JSONObject message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + try { + printString(logLevel, message.toString(INDENT_SPACES)); + } catch (JSONException e) { + log(logLevel, e); + } + } + + private void log(LogLevel logLevel, JSONArray message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + try { + printString(logLevel, message.toString(INDENT_SPACES)); + } catch (JSONException e) { + log(logLevel, e); + } + } + + private void log(LogLevel logLevel, Exception message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + StringBuilder builder = new StringBuilder(); + builder.append(String.valueOf(message)); + builder.append("\n"); + + StackTraceElement[] traces = message.getStackTrace(); + for (StackTraceElement trace : traces) { + builder.append(" at ") + .append(trace.getClassName()) + .append(".") + .append(trace.getMethodName()) + .append("(") + .append(trace.getFileName()) + .append(":") + .append(trace.getLineNumber()) + .append(")") + .append("\n"); + } + + printString(logLevel, builder.toString(), true); + } + + private void log(LogLevel logLevel, Object message) { + if (logLevel.ordinal() < settings.getLogLevel().ordinal()) + return; + + String log; + if (message instanceof byte[]) log = Arrays.toString((byte[]) message); + else if (message instanceof char[]) log = Arrays.toString((char[]) message); + else if (message instanceof short[]) log = Arrays.toString((short[]) message); + else if (message instanceof int[]) log = Arrays.toString((int[]) message); + else if (message instanceof long[]) log = Arrays.toString((long[]) message); + else if (message instanceof float[]) log = Arrays.toString((float[]) message); + else if (message instanceof double[]) log = Arrays.toString((double[]) message); + else if (message instanceof boolean[]) log = Arrays.toString((boolean[]) message); + else if (message instanceof Object[]) log = Arrays.toString((Object[]) message); + else log = String.valueOf(message); + + printString(logLevel, log); + } + + private void printString(LogLevel logLevel, String message) { + printString(logLevel, message, false); + } + + private synchronized void printString(LogLevel logLevel, String message, boolean fromException) { + // Create TAG + String TAG = settings.getTag(); + if (settings.getShowThreadInfo()) TAG += "(" + Thread.currentThread().getName() + ")"; + + // Top Divider + if (settings.getShowDivider()) printLine(logLevel, TAG, TOP_DIVIDER); + + // Log Content + String[] lines = message.split(System.getProperty("line.separator")); + for (String line : lines) + printLine(logLevel, TAG, settings.getShowDivider() ? + "┃ " + line : + line); + + if (settings.getStackTraceCount() > 0 && fromException) + printLine(logLevel, TAG, "Exception occurred"); + + // Middle Divider + if (settings.getShowDivider()) printLine(logLevel, TAG, MIDDLE_DIVIDER); + + // Log Stack Trace + StackTraceElement[] traces = Thread.currentThread().getStackTrace(); + int startIndex = 2; + while (LogUtil.class.getCanonicalName().equals(traces[startIndex].getClassName()) + || LogHelper.class.getCanonicalName().equals(traces[startIndex].getClassName())) + startIndex++; + + for (int i = startIndex; i < Math.min(traces.length, startIndex + settings.getStackTraceCount()); i++) { + StringBuilder builder = new StringBuilder(); + builder.append(" at ") + .append(traces[i].getClassName()) + .append(".") + .append(traces[i].getMethodName()) + .append("(") + .append(traces[i].getFileName()) + .append(":") + .append(traces[i].getLineNumber()) + .append(")"); + + printLine(logLevel, TAG, settings.getShowDivider() ? + "┃ " + builder.toString() : + builder.toString()); + } + + // Log ellipsized stack trance + int leftTraceCount = traces.length - startIndex - settings.getStackTraceCount(); + if (settings.getStackTraceCount() > 0 && leftTraceCount > 1) + printLine(logLevel, TAG, settings.getShowDivider() ? + "┃ at " + leftTraceCount + " more stack traces..." : + " at " + leftTraceCount + " more stack traces..."); + if (settings.getStackTraceCount() > 0 && leftTraceCount == 1) + printLine(logLevel, TAG, settings.getShowDivider() ? + "┃ at 1 more stack trace..." : + " at 1 more stack trace..."); + + // Middle Divider + if (settings.getShowDivider()) printLine(logLevel, TAG, BOTTOM_DIVIDER); + + // LogUtil setToDefault + if (this == LogUtil.getInstance()) setToDefault(); + } + + private void printLine(LogLevel logLevel, String tag, String message) { + switch (logLevel) { + case FULL: + case VERBOSE: + settings.getLogPrinter().v(tag, message); + break; + case DEBUG: + settings.getLogPrinter().d(tag, message); + break; + case INFO: + settings.getLogPrinter().i(tag, message); + break; + case WARN: + settings.getLogPrinter().w(tag, message); + break; + case ERROR: + settings.getLogPrinter().e(tag, message); + break; + case ASSERT: + settings.getLogPrinter().wtf(tag, message); + break; + } + } + + protected void setToDefault() { + settings.setTag(LogUtil.getDefaultSettings().getTag()); + settings.setShowThreadInfo(LogUtil.getDefaultSettings().getShowThreadInfo()); + settings.setStackTraceCount(LogUtil.getDefaultSettings().getStackTraceCount()); + settings.setLogLevel(LogUtil.getDefaultSettings().getLogLevel()); + settings.setShowDivider(LogUtil.getDefaultSettings().getShowDivider()); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/log/LogPrinter.java b/utils/src/main/java/com/thefinestartist/utils/log/LogPrinter.java new file mode 100644 index 0000000..7245ca5 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/log/LogPrinter.java @@ -0,0 +1,40 @@ +package com.thefinestartist.utils.log; + +import android.util.Log; + +import com.thefinestartist.utils.etc.APILevel; + +/** + * LogPrinter helps to print message for {@link LogHelper}. + * + * @author Leonardo Taehwan Kim + */ +public abstract class LogPrinter { + + public void v(String tag, String message) { + Log.v(tag, message); + } + + public void d(String tag, String message) { + Log.d(tag, message); + } + + public void i(String tag, String message) { + Log.i(tag, message); + } + + public void w(String tag, String message) { + Log.w(tag, message); + } + + public void e(String tag, String message) { + Log.e(tag, message); + } + + public void wtf(String tag, String message) { + if (APILevel.require(8)) + Log.wtf(tag, message); + else + Log.e(tag, message); + } +} diff --git a/utils/src/main/java/com/thefinestartist/utils/log/LogUtil.java b/utils/src/main/java/com/thefinestartist/utils/log/LogUtil.java new file mode 100644 index 0000000..5dfcd90 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/log/LogUtil.java @@ -0,0 +1,406 @@ +package com.thefinestartist.utils.log; + +import android.util.Log; + +import androidx.annotation.StringRes; + +import com.thefinestartist.enums.LogLevel; + +import org.json.JSONArray; +import org.json.JSONObject; + +/** + * LogUtil helps to manage application-wide {@link Log} conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class LogUtil { + + // Defaults + private static Settings defaultSettings = new Settings(LogUtil.class.getSimpleName()); + + // Singleton + private static volatile LogHelper logHelper = new LogHelper() + .tag(defaultSettings.getTag()) + .showThreadInfo(defaultSettings.getShowThreadInfo()) + .stackTraceCount(defaultSettings.getStackTraceCount()) + .logLevel(defaultSettings.getLogLevel()) + .showDivider(defaultSettings.getShowDivider()); + + public static Settings getDefaultSettings() { + return defaultSettings; + } + + public static LogHelper getInstance() { + return logHelper; + } + + // Builder + public static LogHelper tag(String tag) { + return logHelper.tag(tag); + } + + public static LogHelper tag(@StringRes int tagRes) { + return logHelper.tag(tagRes); + } + + public static LogHelper tag(Class clazz) { + return logHelper.tag(clazz); + } + + public static LogHelper showThreadInfo(boolean showThreadInfo) { + return logHelper.showThreadInfo(showThreadInfo); + } + + public static LogHelper stackTraceCount(int stackTraceCount) { + return logHelper.stackTraceCount(stackTraceCount); + } + + public static LogHelper logLevel(LogLevel logLevel) { + return logHelper.logLevel(logLevel); + } + + public static LogHelper showDivider(boolean showDivider) { + return logHelper.showDivider(showDivider); + } + + public LogHelper logPrinter(LogPrinter logPrinter) { + return logHelper.logPrinter(logPrinter); + } + + // Logging Verbose + public static void v(byte message) { + logHelper.v(message); + } + + public static void v(char message) { + logHelper.v(message); + } + + public static void v(short message) { + logHelper.v(message); + } + + public static void v(int message) { + logHelper.v(message); + } + + public static void v(long message) { + logHelper.v(message); + } + + public static void v(float message) { + logHelper.v(message); + } + + public static void v(double message) { + logHelper.v(message); + } + + public static void v(boolean message) { + logHelper.v(message); + } + + public static void v(String message) { + logHelper.v(message); + } + + public static void v(JSONObject message) { + logHelper.v(message); + } + + public static void v(JSONArray message) { + logHelper.v(message); + } + + public static void v(Exception message) { + logHelper.v(message); + } + + public static void v(Object message) { + logHelper.v(message); + } + + // Logging Debug + public static void d(byte message) { + logHelper.d(message); + } + + public static void d(char message) { + logHelper.d(message); + } + + public static void d(short message) { + logHelper.d(message); + } + + public static void d(int message) { + logHelper.d(message); + } + + public static void d(long message) { + logHelper.d(message); + } + + public static void d(float message) { + logHelper.d(message); + } + + public static void d(double message) { + logHelper.d(message); + } + + public static void d(boolean message) { + logHelper.d(message); + } + + public static void d(String message) { + logHelper.d(message); + } + + public static void d(JSONObject message) { + logHelper.d(message); + } + + public static void d(JSONArray message) { + logHelper.d(message); + } + + public static void d(Exception message) { + logHelper.d(message); + } + + public static void d(Object message) { + logHelper.d(message); + } + + // Logging Information + public static void i(byte message) { + logHelper.i(message); + } + + public static void i(char message) { + logHelper.i(message); + } + + public static void i(short message) { + logHelper.i(message); + } + + public static void i(int message) { + logHelper.i(message); + } + + public static void i(long message) { + logHelper.i(message); + } + + public static void i(float message) { + logHelper.i(message); + } + + public static void i(double message) { + logHelper.i(message); + } + + public static void i(boolean message) { + logHelper.i(message); + } + + public static void i(String message) { + logHelper.i(message); + } + + public static void i(JSONObject message) { + logHelper.i(message); + } + + public static void i(JSONArray message) { + logHelper.i(message); + } + + public static void i(Exception message) { + logHelper.i(message); + } + + public static void i(Object message) { + logHelper.i(message); + } + + // Logging Warning + public static void w(byte message) { + logHelper.w(message); + } + + public static void w(char message) { + logHelper.w(message); + } + + public static void w(short message) { + logHelper.w(message); + } + + public static void w(int message) { + logHelper.w(message); + } + + public static void w(long message) { + logHelper.w(message); + } + + public static void w(float message) { + logHelper.w(message); + } + + public static void w(double message) { + logHelper.w(message); + } + + public static void w(boolean message) { + logHelper.w(message); + } + + public static void w(String message) { + logHelper.w(message); + } + + public static void w(JSONObject message) { + logHelper.w(message); + } + + public static void w(JSONArray message) { + logHelper.w(message); + } + + public static void w(Exception message) { + logHelper.w(message); + } + + public static void w(Object message) { + logHelper.w(message); + } + + // Logging Error + public static void e(byte message) { + logHelper.e(message); + } + + public static void e(char message) { + logHelper.e(message); + } + + public static void e(short message) { + logHelper.e(message); + } + + public static void e(int message) { + logHelper.e(message); + } + + public static void e(long message) { + logHelper.e(message); + } + + public static void e(float message) { + logHelper.e(message); + } + + public static void e(double message) { + logHelper.e(message); + } + + public static void e(boolean message) { + logHelper.e(message); + } + + public static void e(String message) { + logHelper.e(message); + } + + public static void e(JSONObject message) { + logHelper.e(message); + } + + public static void e(JSONArray message) { + logHelper.e(message); + } + + public static void e(Exception message) { + logHelper.e(message); + } + + public static void e(Object message) { + logHelper.e(message); + } + + // Logging Assert + public static void wtf(byte message) { + logHelper.wtf(message); + } + + public static void wtf(char message) { + logHelper.wtf(message); + } + + public static void wtf(short message) { + logHelper.wtf(message); + } + + public static void wtf(int message) { + logHelper.wtf(message); + } + + public static void wtf(long message) { + logHelper.wtf(message); + } + + public static void wtf(float message) { + logHelper.wtf(message); + } + + public static void wtf(double message) { + logHelper.wtf(message); + } + + public static void wtf(boolean message) { + logHelper.wtf(message); + } + + public static void wtf(String message) { + logHelper.wtf(message); + } + + public static void wtf(JSONObject message) { + logHelper.wtf(message); + } + + public static void wtf(JSONArray message) { + logHelper.wtf(message); + } + + public static void wtf(Exception message) { + logHelper.wtf(message); + } + + public static void wtf(Object message) { + logHelper.wtf(message); + } + + // Logging JsonString + public static void json(String jsonString) { + json(LogLevel.DEBUG, jsonString); + } + + public static void json(LogLevel logLevel, String jsonString) { + logHelper.json(logLevel, jsonString); + } + + // Logging XmlString + public static void xml(String xmlString) { + xml(LogLevel.DEBUG, xmlString); + } + + public static void xml(LogLevel logLevel, String jsonString) { + logHelper.xml(logLevel, jsonString); + } +} diff --git a/utils/src/main/java/com/thefinestartist/utils/log/Settings.java b/utils/src/main/java/com/thefinestartist/utils/log/Settings.java new file mode 100644 index 0000000..ca68a98 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/log/Settings.java @@ -0,0 +1,109 @@ +package com.thefinestartist.utils.log; + + +import androidx.annotation.StringRes; + +import com.thefinestartist.enums.LogLevel; +import com.thefinestartist.utils.content.ResourcesUtil; + +/** + * Settings for {@link LogHelper}. + * + * @author Leonardo Taehwan Kim + */ +public class Settings { + + private String tag = Settings.class.getSimpleName(); + private boolean showThreadInfo = false; + private int stackTraceCount = 0; + private LogLevel logLevel = LogLevel.FULL; + private boolean showDivider = false; + private LogPrinter logPrinter = new AndroidLogPrinter(); + + public Settings() { + } + + public Settings(String tag) { + this.tag = tag; + } + + public Settings(@StringRes int tagRes) { + this.tag = ResourcesUtil.getString(tagRes); + } + + public Settings(Class clazz) { + this.tag = clazz.getSimpleName(); + } + + public String getTag() { + return tag; + } + + public Settings setTag(String tag) { + this.tag = tag; + if (this == LogUtil.getDefaultSettings()) LogUtil.getInstance().setToDefault(); + return this; + } + + public Settings setTag(@StringRes int tagRes) { + this.tag = ResourcesUtil.getString(tagRes); + if (this == LogUtil.getDefaultSettings()) LogUtil.getInstance().setToDefault(); + return this; + } + + public Settings setTag(Class clazz) { + this.tag = clazz.getSimpleName(); + if (this == LogUtil.getDefaultSettings()) LogUtil.getInstance().setToDefault(); + return this; + } + + public boolean getShowThreadInfo() { + return showThreadInfo; + } + + public Settings setShowThreadInfo(boolean showThreadInfo) { + this.showThreadInfo = showThreadInfo; + if (this == LogUtil.getDefaultSettings()) LogUtil.getInstance().setToDefault(); + return this; + } + + public int getStackTraceCount() { + return stackTraceCount; + } + + public Settings setStackTraceCount(int stackTraceCount) { + this.stackTraceCount = stackTraceCount; + if (this == LogUtil.getDefaultSettings()) LogUtil.getInstance().setToDefault(); + return this; + } + + public LogLevel getLogLevel() { + return logLevel; + } + + public Settings setLogLevel(LogLevel logLevel) { + this.logLevel = logLevel; + if (this == LogUtil.getDefaultSettings()) LogUtil.getInstance().setToDefault(); + return this; + } + + public boolean getShowDivider() { + return showDivider; + } + + public Settings setShowDivider(boolean showDivider) { + this.showDivider = showDivider; + if (this == LogUtil.getDefaultSettings()) LogUtil.getInstance().setToDefault(); + return this; + } + + public LogPrinter getLogPrinter() { + return logPrinter; + } + + public Settings setLogPrinter(LogPrinter logPrinter) { + this.logPrinter = logPrinter; + if (this == LogUtil.getDefaultSettings()) LogUtil.getInstance().setToDefault(); + return this; + } +} diff --git a/utils/src/main/java/com/thefinestartist/utils/preferences/Pref.java b/utils/src/main/java/com/thefinestartist/utils/preferences/Pref.java new file mode 100644 index 0000000..fd661c6 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/preferences/Pref.java @@ -0,0 +1,9 @@ +package com.thefinestartist.utils.preferences; + +/** + * Pref is abbreviation class of {@link PreferencesUtil}. + * + * @author Robin Gustafsson + */ +public class Pref extends PreferencesUtil { +} diff --git a/utils/src/main/java/com/thefinestartist/utils/preferences/PreferencesUtil.java b/utils/src/main/java/com/thefinestartist/utils/preferences/PreferencesUtil.java new file mode 100755 index 0000000..ee68059 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/preferences/PreferencesUtil.java @@ -0,0 +1,271 @@ +package com.thefinestartist.utils.preferences; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; +import android.util.Base64; + +import com.thefinestartist.Base; +import com.thefinestartist.utils.etc.APILevel; +import com.thefinestartist.utils.log.LogHelper; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Set; + +/** + * PreferencesUtil helps to manage application-wide {@link SharedPreferences} conveniently. + * + * @author Robin Gustafsson + */ +public class PreferencesUtil { + + private static final LogHelper LogHelper = new LogHelper(PreferencesUtil.class); + private static String defaultName = PreferencesUtil.class.getCanonicalName(); + + private static SharedPreferences getPreferences(String name) { + return Base.getContext().getSharedPreferences(name, Context.MODE_PRIVATE); + } + + + public static String getDefaultName() { + return defaultName; + } + + public static void setDefaultName(String name) { + defaultName = name; + } + + + public static boolean get(String key, boolean defValue) { + return get(defaultName, key, defValue); + } + + public static int get(String key, int defValue) { + return get(defaultName, key, defValue); + } + + public static float get(String key, float defValue) { + return get(defaultName, key, defValue); + } + + public static long get(String key, long defValue) { + return get(defaultName, key, defValue); + } + + public static String get(String key, String defValue) { + return get(defaultName, key, defValue); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static Set get(String key, Set defValue) { + return get(defaultName, key, defValue); + } + + @TargetApi(Build.VERSION_CODES.FROYO) + public static C get(String key, C defValue) { + return get(defaultName, key, defValue); + } + + public static boolean get(String name, String key, boolean defValue) { + return getPreferences(name).getBoolean(key, defValue); + } + + public static int get(String name, String key, int defValue) { + return getPreferences(name).getInt(key, defValue); + } + + public static float get(String name, String key, float defValue) { + return getPreferences(name).getFloat(key, defValue); + } + + public static long get(String name, String key, long defValue) { + return getPreferences(name).getLong(key, defValue); + } + + public static String get(String name, String key, String defValue) { + return getPreferences(name).getString(key, defValue); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static Set get(String name, String key, Set defValue) { + return getPreferences(name).getStringSet(key, defValue); + } + + @TargetApi(Build.VERSION_CODES.FROYO) + public static C get(String name, String key, C defValue) { + ByteArrayInputStream bais = null; + ObjectInputStream ois = null; + C result = defValue; + + String value = getPreferences(name).getString(key, null); + if (value != null) { + try { + byte[] decoded = Base64.decode(value.getBytes(), Base64.DEFAULT); + bais = new ByteArrayInputStream(decoded); + ois = new ObjectInputStream(bais); + result = (C) ois.readObject(); + + } catch (Exception e) { + LogHelper.e(e); + } finally { + if (ois != null) { + try { + ois.close(); + } catch (IOException e) { + LogHelper.e(e); + } + } + if (bais != null) { + try { + bais.close(); + } catch (IOException e) { + LogHelper.e(e); + } + } + } + } + + return result; + } + + + public static void put(String key, boolean value) { + put(defaultName, key, value); + } + + public static void put(String key, int value) { + put(defaultName, key, value); + } + + public static void put(String key, float value) { + put(defaultName, key, value); + } + + public static void put(String key, long value) { + put(defaultName, key, value); + } + + public static void put(String key, String value) { + put(defaultName, key, value); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static void put(String key, Set value) { + put(defaultName, key, value); + } + + @TargetApi(Build.VERSION_CODES.FROYO) + public static void put(String key, C value) { + put(defaultName, key, value); + } + + public static void put(String name, String key, boolean value) { + if (APILevel.require(9)) + getPreferences(name).edit().putBoolean(key, value).apply(); + else + getPreferences(name).edit().putBoolean(key, value).commit(); + } + + public static void put(String name, String key, int value) { + if (APILevel.require(9)) + getPreferences(name).edit().putInt(key, value).apply(); + else + getPreferences(name).edit().putInt(key, value).commit(); + } + + public static void put(String name, String key, float value) { + if (APILevel.require(9)) + getPreferences(name).edit().putFloat(key, value).apply(); + else + getPreferences(name).edit().putFloat(key, value).commit(); + } + + public static void put(String name, String key, long value) { + if (APILevel.require(9)) + getPreferences(name).edit().putLong(key, value).apply(); + else + getPreferences(name).edit().putLong(key, value).commit(); + } + + public static void put(String name, String key, String value) { + if (APILevel.require(9)) + getPreferences(name).edit().putString(key, value).apply(); + else + getPreferences(name).edit().putString(key, value).commit(); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static void put(String name, String key, Set value) { + if (APILevel.require(9)) + getPreferences(name).edit().putStringSet(key, value).apply(); + else + getPreferences(name).edit().putStringSet(key, value).commit(); + } + + @TargetApi(Build.VERSION_CODES.FROYO) + public static void put(String name, String key, C value) { + ByteArrayOutputStream baos = null; + ObjectOutputStream oos = null; + + try { + baos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(baos); + oos.writeObject(value); + byte[] encoded = Base64.encode(baos.toByteArray(), Base64.DEFAULT); + if (APILevel.require(9)) + getPreferences(name).edit().putString(key, new String(encoded)).apply(); + else + getPreferences(name).edit().putString(key, new String(encoded)).commit(); + + } catch (IOException e) { + LogHelper.e(e); + throw new RuntimeException(e); + + } finally { + if (oos != null) { + try { + oos.close(); + } catch (IOException e) { + LogHelper.e(e); + } + } + if (baos != null) { + try { + baos.close(); + } catch (IOException e) { + LogHelper.e(e); + } + } + } + } + + + public static void remove(String key) { + remove(defaultName, key); + } + + public static void remove(String name, String key) { + if (APILevel.require(9)) + getPreferences(name).edit().remove(key).apply(); + else + getPreferences(name).edit().remove(key).commit(); + } + + + public static void clear() { + clear(defaultName); + } + + public static void clear(String name) { + if (APILevel.require(9)) + getPreferences(name).edit().clear().apply(); + else + getPreferences(name).edit().clear().commit(); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/service/ClipboardManagerUtil.java b/utils/src/main/java/com/thefinestartist/utils/service/ClipboardManagerUtil.java new file mode 100644 index 0000000..4c3fff3 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/service/ClipboardManagerUtil.java @@ -0,0 +1,57 @@ +package com.thefinestartist.utils.service; + +import android.content.ClipData; +import android.content.ClipDescription; +import android.content.ClipboardManager; + +import com.thefinestartist.utils.etc.APILevel; + +/** + * ClipboardManagerUtil helps to manage {@link ClipboardManager} conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class ClipboardManagerUtil { + + public static void setText(CharSequence text) { + android.text.ClipboardManager clipboardManager = ServiceUtil.getClipboardManager(); + if (APILevel.require(11)) { + ClipboardManager cm = (ClipboardManager) clipboardManager; + ClipData clip = ClipData.newPlainText("ClipboardManagerUtil", text); + cm.setPrimaryClip(clip); + } else { + clipboardManager.setText(text); + } + } + + public static boolean hasText() { + android.text.ClipboardManager clipboardManager = ServiceUtil.getClipboardManager(); + if (APILevel.require(11)) { + ClipboardManager cm = (ClipboardManager) clipboardManager; + ClipDescription description = cm.getPrimaryClipDescription(); + ClipData clipData = cm.getPrimaryClip(); + return clipData != null + && description != null + && (description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)); + } else { + return clipboardManager.hasText(); + } + } + + public static CharSequence getText() { + android.text.ClipboardManager clipboardManager = ServiceUtil.getClipboardManager(); + if (APILevel.require(11)) { + ClipboardManager cm = (ClipboardManager) clipboardManager; + ClipDescription description = cm.getPrimaryClipDescription(); + ClipData clipData = cm.getPrimaryClip(); + if (clipData != null + && description != null + && description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) + return clipData.getItemAt(0).getText(); + else + return null; + } else { + return clipboardManager.getText(); + } + } +} diff --git a/utils/src/main/java/com/thefinestartist/utils/service/ServiceUtil.java b/utils/src/main/java/com/thefinestartist/utils/service/ServiceUtil.java new file mode 100644 index 0000000..5557e21 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/service/ServiceUtil.java @@ -0,0 +1,330 @@ +package com.thefinestartist.utils.service; + +import android.accounts.AccountManager; +import android.annotation.TargetApi; +import android.app.ActivityManager; +import android.app.AlarmManager; +import android.app.AppOpsManager; +import android.app.DownloadManager; +import android.app.KeyguardManager; +import android.app.NotificationManager; +import android.app.SearchManager; +import android.app.UiModeManager; +import android.app.WallpaperManager; +import android.app.admin.DevicePolicyManager; +import android.app.job.JobScheduler; +import android.app.usage.NetworkStatsManager; +import android.app.usage.UsageStatsManager; +import android.appwidget.AppWidgetManager; +import android.bluetooth.BluetoothManager; +import android.content.Context; +import android.content.RestrictionsManager; +import android.content.pm.LauncherApps; +import android.hardware.ConsumerIrManager; +import android.hardware.SensorManager; +import android.hardware.camera2.CameraManager; +import android.hardware.display.DisplayManager; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.input.InputManager; +import android.hardware.usb.UsbManager; +import android.location.LocationManager; +import android.media.AudioManager; +import android.media.MediaRouter; +import android.media.midi.MidiManager; +import android.media.projection.MediaProjectionManager; +import android.media.session.MediaSessionManager; +import android.media.tv.TvInputManager; +import android.net.ConnectivityManager; +import android.net.nsd.NsdManager; +import android.net.wifi.WifiManager; +import android.net.wifi.p2p.WifiP2pManager; +import android.nfc.NfcManager; +import android.os.BatteryManager; +import android.os.DropBoxManager; +import android.os.PowerManager; +import android.os.UserManager; +import android.os.Vibrator; +import android.os.storage.StorageManager; +import android.print.PrintManager; +import android.telecom.TelecomManager; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.ClipboardManager; +import android.view.LayoutInflater; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.CaptioningManager; +import android.view.inputmethod.InputMethodManager; +import android.view.textservice.TextServicesManager; + +import androidx.annotation.NonNull; + +import com.thefinestartist.Base; + +/** + * ServiceUtil helps to manage Android system service conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class ServiceUtil { + + public static Object getSystemService(@NonNull String serviceName) { + return Base.getContext().getSystemService(serviceName); + } + + public static AccessibilityManager getAccessibilityManager() { + return (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE); + } + + @TargetApi(19) + public static CaptioningManager getCaptioningManager() { + return (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); + } + + public static AccountManager getAccountManager() { + return (AccountManager) getSystemService(Context.ACCOUNT_SERVICE); + } + + public static ActivityManager getActivityManager() { + return (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + } + + public static AlarmManager getAlarmManager() { + return (AlarmManager) getSystemService(Context.ALARM_SERVICE); + } + + public static AudioManager getAudioManager() { + return (AudioManager) getSystemService(Context.AUDIO_SERVICE); + } + + @TargetApi(16) + public static MediaRouter getMediaRouter() { + return (MediaRouter) getSystemService(Context.MEDIA_ROUTER_SERVICE); + } + + @TargetApi(18) + public static BluetoothManager getBluetoothManager() { + return (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + } + + public static ClipboardManager getClipboardManager() { + return (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + } + + public static ConnectivityManager getConnectivityManager() { + return (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + } + + @TargetApi(8) + public static DevicePolicyManager getDevicePolicyManager() { + return (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); + } + + @TargetApi(9) + public static DownloadManager getDownloadManager() { + return (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); + } + + @TargetApi(21) + public static BatteryManager getBatteryManager() { + return (BatteryManager) getSystemService(Context.BATTERY_SERVICE); + } + + @TargetApi(10) + public static NfcManager getNfcManager() { + return (NfcManager) getSystemService(Context.NFC_SERVICE); + } + + @TargetApi(8) + public static DropBoxManager getDropBoxManager() { + return (DropBoxManager) getSystemService(Context.DROPBOX_SERVICE); + } + + @TargetApi(16) + public static InputManager getInputManager() { + return (InputManager) getSystemService(Context.INPUT_SERVICE); + } + + @TargetApi(17) + public static DisplayManager getDisplayManager() { + return (DisplayManager) getSystemService(Context.DISPLAY_SERVICE); + } + + public static InputMethodManager getInputMethodManager() { + return (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + } + + @TargetApi(14) + public static TextServicesManager getTextServicesManager() { + return (TextServicesManager) getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); + } + + public static KeyguardManager getKeyguardManager() { + return (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); + } + + public static LayoutInflater getLayoutInflater() { + return (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + public static LocationManager getLocationManager() { + return (LocationManager) getSystemService(Context.LOCATION_SERVICE); + } + + public static NotificationManager getNotificationManager() { + return (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + } + + @TargetApi(16) + public static NsdManager getNsdManager() { + return (NsdManager) getSystemService(Context.NSD_SERVICE); + } + + public static PowerManager getPowerManager() { + return (PowerManager) getSystemService(Context.POWER_SERVICE); + } + + public static SearchManager getSearchManager() { + return (SearchManager) getSystemService(Context.SEARCH_SERVICE); + } + + public static SensorManager getSensorManager() { + return (SensorManager) getSystemService(Context.SENSOR_SERVICE); + } + + @TargetApi(9) + public static StorageManager getStorageManager() { + return (StorageManager) getSystemService(Context.STORAGE_SERVICE); + } + + public static TelephonyManager getTelephonyManager() { + return (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + } + + @TargetApi(22) + public static SubscriptionManager getSubscriptionManager() { + return (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + } + + @TargetApi(23) + public static CarrierConfigManager getCarrierConfigManager() { + return (CarrierConfigManager) getSystemService(Context.CARRIER_CONFIG_SERVICE); + } + + @TargetApi(21) + public static TelecomManager getTelecomManager() { + return (TelecomManager) getSystemService(Context.TELECOM_SERVICE); + } + + @TargetApi(8) + public static UiModeManager getUiModeManager() { + return (UiModeManager) getSystemService(Context.UI_MODE_SERVICE); + } + + @TargetApi(12) + public static UsbManager getUsbManager() { + return (UsbManager) getSystemService(Context.USB_SERVICE); + } + + public static Vibrator getVibrator() { + return (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); + } + + public static WallpaperManager getWallpaperManager() { + return WallpaperManager.getInstance(Base.getContext()); + } + + public static WifiManager getWifiManager() { + return (WifiManager) getSystemService(Context.WIFI_SERVICE); + } + + @TargetApi(14) + public static WifiP2pManager getWifiP2pManager() { + return (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); + } + + public static WindowManager getWindowManager() { + return (WindowManager) getSystemService(Context.WINDOW_SERVICE); + } + + @TargetApi(17) + public static UserManager getUserManager() { + return (UserManager) getSystemService(Context.USER_SERVICE); + } + + @TargetApi(19) + public static AppOpsManager getAppOpsManager() { + return (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); + } + + @TargetApi(21) + public static CameraManager getCameraManager() { + return (CameraManager) getSystemService(Context.CAMERA_SERVICE); + } + + @TargetApi(21) + public static LauncherApps getLauncherApps() { + return (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE); + } + + @TargetApi(21) + public static RestrictionsManager getRestrictionsManager() { + return (RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE); + } + + @TargetApi(19) + public static PrintManager getPrintManager() { + return (PrintManager) getSystemService(Context.PRINT_SERVICE); + } + + @TargetApi(19) + public static ConsumerIrManager getConsumerIrManager() { + return (ConsumerIrManager) getSystemService(Context.CONSUMER_IR_SERVICE); + } + + @TargetApi(21) + public static MediaSessionManager getMediaSessionManager() { + return (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE); + } + + @TargetApi(23) + public static FingerprintManager getFingerprintManager() { + return (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE); + } + + @TargetApi(21) + public static TvInputManager getTvInputManager() { + return (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE); + } + + @TargetApi(22) + public static UsageStatsManager getUsageStatsManager() { + return (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); + } + + @TargetApi(23) + public static NetworkStatsManager getNetworkStatsManager() { + return (NetworkStatsManager) getSystemService(Context.NETWORK_STATS_SERVICE); + } + + @TargetApi(21) + public static JobScheduler getJobScheduler() { + return (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); + } + + @TargetApi(21) + public static MediaProjectionManager getMediaProjectionManager() { + return (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); + } + + @TargetApi(21) + public static AppWidgetManager getAppWidgetManager() { + return (AppWidgetManager) getSystemService(Context.APPWIDGET_SERVICE); + } + + @TargetApi(23) + public static MidiManager getMidiManager() { + return (MidiManager) getSystemService(Context.MIDI_SERVICE); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/service/VibratorUtil.java b/utils/src/main/java/com/thefinestartist/utils/service/VibratorUtil.java new file mode 100755 index 0000000..e24dbca --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/service/VibratorUtil.java @@ -0,0 +1,48 @@ +package com.thefinestartist.utils.service; + +import android.annotation.TargetApi; +import android.media.AudioAttributes; +import android.os.Vibrator; + +/** + * VibratorUtil helps to manage {@link Vibrator} conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class VibratorUtil { + + @TargetApi(11) + public static boolean hasVibrator() { + return ServiceUtil.getVibrator().hasVibrator(); + } + + public static void vibrate() { + vibrate(200); + } + + public static void vibrate(long milliseconds) { + vibrate(new long[]{milliseconds}); + } + + public static void vibrate(long[] pattern) { + vibrate(pattern, -1); + } + + public static void vibrate(long[] pattern, int repeat) { + ServiceUtil.getVibrator().vibrate(pattern, repeat); + } + + @TargetApi(21) + public static void vibrate(long milliseconds, AudioAttributes attributes) { + vibrate(new long[]{milliseconds}, -1, attributes); + } + + @TargetApi(21) + public static void vibrate(long[] pattern, int repeat, AudioAttributes attributes) { + ServiceUtil.getVibrator().vibrate(pattern, repeat, attributes); + } + + public static void cancel() { + ServiceUtil.getVibrator().cancel(); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/service/WindowManagerUtil.java b/utils/src/main/java/com/thefinestartist/utils/service/WindowManagerUtil.java new file mode 100644 index 0000000..dad44b8 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/service/WindowManagerUtil.java @@ -0,0 +1,21 @@ +package com.thefinestartist.utils.service; + +import android.view.Display; +import android.view.View; +import android.view.WindowManager; + +/** + * WindowManagerUtil helps to manage {@link WindowManager} conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class WindowManagerUtil { + + public static Display getDefaultDisplay() { + return ServiceUtil.getWindowManager().getDefaultDisplay(); + } + + public static void removeViewImmediate(View view) { + ServiceUtil.getWindowManager().removeViewImmediate(view); + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/ui/DisplayUtil.java b/utils/src/main/java/com/thefinestartist/utils/ui/DisplayUtil.java new file mode 100644 index 0000000..ddc38f4 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/ui/DisplayUtil.java @@ -0,0 +1,82 @@ +package com.thefinestartist.utils.ui; + +import android.graphics.Point; +import android.util.TypedValue; +import android.view.Display; + +import com.thefinestartist.enums.Rotation; +import com.thefinestartist.utils.content.ResourcesUtil; +import com.thefinestartist.utils.content.ThemeUtil; +import com.thefinestartist.utils.content.TypedValueUtil; +import com.thefinestartist.utils.etc.APILevel; +import com.thefinestartist.utils.service.WindowManagerUtil; + +/** + * DisplayUtil helps to calculate screen size conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class DisplayUtil { + + public static int getWidth() { + Display display = WindowManagerUtil.getDefaultDisplay(); + if (APILevel.require(13)) { + Point size = new Point(); + display.getSize(size); + return size.x; + } else { + return display.getWidth(); + } + } + + public static int getHeight() { + Display display = WindowManagerUtil.getDefaultDisplay(); + if (APILevel.require(13)) { + Point size = new Point(); + display.getSize(size); + return size.y; + } else { + return display.getHeight(); + } + } + + public static Rotation getRotation() { + if (APILevel.require(8)) + return Rotation.fromValue(WindowManagerUtil.getDefaultDisplay().getRotation()); + else + return Rotation.fromValue(WindowManagerUtil.getDefaultDisplay().getOrientation()); + } + + public static boolean isPortrait() { + return getHeight() >= getWidth(); + } + + public static boolean isLandscape() { + return getHeight() < getWidth(); + } + + public static int getStatusBarHeight() { + int resourceId = ResourcesUtil.getIdentifier("status_bar_height", "dimen", "android"); + return resourceId > 0 ? + ResourcesUtil.getDimensionPixelSize(resourceId) : + 0; + } + + public static int getToolbarHeight() { + return getActionBarHeight(); + } + + public static int getActionBarHeight() { + TypedValue tv = new TypedValue(); + return ThemeUtil.resolveAttribute(android.R.attr.actionBarSize, tv, true) ? + TypedValueUtil.complexToDimensionPixelSize(tv.data) : + 0; + } + + public static int getNavigationBarHeight() { + int resourceId = ResourcesUtil.getIdentifier("navigation_bar_height", "dimen", "android"); + return resourceId > 0 ? + ResourcesUtil.getDimensionPixelSize(resourceId) : + 0; + } +} diff --git a/utils/src/main/java/com/thefinestartist/utils/ui/Keyboard.java b/utils/src/main/java/com/thefinestartist/utils/ui/Keyboard.java new file mode 100644 index 0000000..ff224cc --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/ui/Keyboard.java @@ -0,0 +1,9 @@ +package com.thefinestartist.utils.ui; + +/** + * Keyboard is abbreviation class of {@link KeyboardUtil}. + * + * @author Leonardo Taehwan Kim + */ +public class Keyboard extends KeyboardUtil { +} diff --git a/utils/src/main/java/com/thefinestartist/utils/ui/KeyboardUtil.java b/utils/src/main/java/com/thefinestartist/utils/ui/KeyboardUtil.java new file mode 100644 index 0000000..9ae42cc --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/ui/KeyboardUtil.java @@ -0,0 +1,184 @@ +package com.thefinestartist.utils.ui; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; + +import androidx.fragment.app.Fragment; + +import com.thefinestartist.Base; +import com.thefinestartist.converters.UnitConverter; +import com.thefinestartist.utils.etc.ThreadUtil; +import com.thefinestartist.utils.service.ServiceUtil; + +/** + * KeyboardUtil helps to show and hide keyboard conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class KeyboardUtil { + + public static int height = 0; + public static final String KEYBOARD_UTIL_PREF = "KEYBOARD_UTIL_PREF"; + public static final String KEYBOARD_HEIGHT = "KEYBOARD_HEIGHT"; + public static final int DEFAULT_KEYBOARD_HEIGHT = 200; + + /** + * Helps to show keyboard in {@link Activity#onCreate(Bundle)}, {@link Activity#onStart()}, + * {@link Activity#onResume()}, + * {@link MenuItem.OnActionExpandListener#onMenuItemActionExpand(MenuItem)}, + * {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)} and etc + * This method guarantee to show keyboard every time. + */ + public static void show(final View view) { + if (view == null) + return; + + view.postDelayed(new Runnable() { + @Override + public void run() { + showInMainThread(view); + } + }, 200); + } + + /** + * Please note that this method does not guarantee to show keyboard every time. To guarantee + * to show keyboard, please use {@link #show(View)} instead. It doesn't have any delay, use + * this method when it is able to show keyboard immediately. EX) when user click a button to + * show keyboard + */ + public static void showImmediately(final View view) { + if (view == null) + return; + + if (ThreadUtil.isMain()) { + showInMainThread(view); + } else { + view.post(new Runnable() { + @Override + public void run() { + showInMainThread(view); + } + }); + } + } + + private static void showInMainThread(final View view) { + if (view == null) + return; + + view.requestFocus(); + ServiceUtil.getInputMethodManager().showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); + } + + public static void hide(Fragment fragment) { + if (fragment == null || fragment.getActivity() == null) + return; + + hide(fragment.getActivity()); + } + + public static void hide(Fragment fragment, boolean clearFocus) { + if (fragment == null || fragment.getActivity() == null) + return; + + hide(fragment.getActivity()); + } + + public static void hide(Activity activity) { + hide(activity, true); + } + + public static void hide(Activity activity, boolean clearFocus) { + if (activity == null) + return; + + hide(activity.getCurrentFocus(), clearFocus); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static void hide(android.app.Fragment fragment) { + hide(fragment, true); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static void hide(android.app.Fragment fragment, boolean clearFocus) { + if (fragment == null || fragment.getActivity() == null) + return; + + hide(fragment.getActivity(), clearFocus); + } + + public static void hide(Dialog dialog) { + hide(dialog, true); + } + + public static void hide(Dialog dialog, boolean clearFocus) { + if (dialog == null) + return; + + hide(dialog.getCurrentFocus(), clearFocus); + } + + public static void hide(View view) { + hide(view, true); + } + + public static void hide(View view, boolean clearFocus) { + if (view == null) + return; + + if (clearFocus) { + view.clearFocus(); + } + + ServiceUtil.getInputMethodManager().hideSoftInputFromWindow(view.getWindowToken(), 0); + } + + public static int getHeight() { + if (height <= 0) + height = Base.getContext().getSharedPreferences(KEYBOARD_UTIL_PREF, Context.MODE_PRIVATE).getInt(KEYBOARD_HEIGHT, UnitConverter.dpToPx(DEFAULT_KEYBOARD_HEIGHT)); + + return height; + } + + public static void setHeight(int height) { + KeyboardUtil.height = height; + Base.getContext().getSharedPreferences(KEYBOARD_UTIL_PREF, Context.MODE_PRIVATE).edit().putInt(KEYBOARD_HEIGHT, height).apply(); + } + +// coordinatorLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { +// @Override +// public void onGlobalLayout() { +// Rect r = new Rect(); +// coordinatorLayout.getWindowVisibleDisplayFrame(r); +// if (ResourcesUtil.navigationBarHeight == -1) { +// ResourcesUtil.navigationBarHeight = coordinatorLayout.getRootView().getHeight() - r.height() - ResourcesUtil.statusBarHeight; +// } +// int usableHeight = coordinatorLayout.getRootView().getHeight() - ResourcesUtil.statusBarHeight - ResourcesUtil.navigationBarHeight; +// int keyboardHeight = usableHeight - r.height(); +// if (isKeyboardOpened) { +// if (keyboardHeight < 100) { +// onKeyboardChanged(usableHeight, keyboardHeight, false); +// isKeyboardOpened = false; +// } +// } else { +// if (keyboardHeight > 100) { +// onKeyboardChanged(usableHeight, keyboardHeight, true); +// isKeyboardOpened = true; +// } +// } +// } +// }); +} +//TODO: Support keyboard show and hide listener +//TODO: Keyboard height \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/utils/ui/ViewUtil.java b/utils/src/main/java/com/thefinestartist/utils/ui/ViewUtil.java new file mode 100644 index 0000000..3847279 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/utils/ui/ViewUtil.java @@ -0,0 +1,39 @@ +package com.thefinestartist.utils.ui; + +import android.graphics.drawable.Drawable; +import android.view.View; + +import androidx.annotation.DrawableRes; + +import com.thefinestartist.Base; +import com.thefinestartist.utils.etc.APILevel; + +/** + * ViewUtil helps to set background drawable conveniently. + * + * @author Leonardo Taehwan Kim + */ +public class ViewUtil { + + public static void setBackground(View view, Drawable drawable) { + if (view == null) + return; + + if (APILevel.require(16)) { + view.setBackground(drawable); + } else { + view.setBackgroundDrawable(drawable); + } + } + + public static void setBackground(View view, @DrawableRes int drawableRes) { + if (view == null) + return; + + if (APILevel.require(16)) { + view.setBackground(Base.getResources().getDrawable(drawableRes)); + } else { + view.setBackgroundDrawable(Base.getResources().getDrawable(drawableRes)); + } + } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/wip/AgeUtil.java b/utils/src/main/java/com/thefinestartist/wip/AgeUtil.java new file mode 100755 index 0000000..0856f9f --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/AgeUtil.java @@ -0,0 +1,29 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist + */ +public class AgeUtil { + +// public static String getFromBirthDay(Date date) { +// if (date == null) +// return "?"; +// +// int year = Integer.parseInt((String) DateFormat.format("yyyy", date)); +// int month = Integer.parseInt((String) DateFormat.format("MM", date)); +// int day = Integer.parseInt((String) DateFormat.format("dd", date)); +// +// Calendar birthday = Calendar.getInstance(); +// birthday.set(year, month - 1, day); +// Calendar today = Calendar.getInstance(); +// +// +// int age = today.get(Calendar.YEAR) - birthday.get(Calendar.YEAR); +// +// if (today.get(Calendar.DAY_OF_YEAR) < birthday.get(Calendar.DAY_OF_YEAR)) { +// age--; +// } +// +// return String.format("%d", age); +// } +} diff --git a/utils/src/main/java/com/thefinestartist/wip/AudioManagerUtil.java b/utils/src/main/java/com/thefinestartist/wip/AudioManagerUtil.java new file mode 100644 index 0000000..73c04d4 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/AudioManagerUtil.java @@ -0,0 +1,42 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist on 2/18/16. + */ +public class AudioManagerUtil { + +// public static void getMode() { +// AudioManager am = ServiceUtil.getAudioManager(); +// switch (am.getRingerMode()) { +// case AudioManager.RINGER_MODE_NORMAL: +// case AudioManager.RINGER_MODE_VIBRATE: +// case AudioManager.RINGER_MODE_SILENT: +// } +// } +// +// public static void getVolume() { +// AudioManager am = ServiceUtil.getAudioManager(); +// int voiceCall = am.getStreamVolume(AudioManager.STREAM_VOICE_CALL); +// int system = am.getStreamVolume(AudioManager.STREAM_SYSTEM); +// int ring = am.getStreamVolume(AudioManager.STREAM_RING); +// int music = am.getStreamVolume(AudioManager.STREAM_MUSIC); +// int alarm = am.getStreamVolume(AudioManager.STREAM_ALARM); +// int notification = am.getStreamVolume(AudioManager.STREAM_NOTIFICATION); +// } +// +// public static void setVolume() { +// AudioManager am = ServiceUtil.getAudioManager(); +// int currentVolumn = am.getStreamVolume(AudioManager.STREAM_SYSTEM); +// switch (am.getRingerMode()) { +// case AudioManager.RINGER_MODE_SILENT: +// am.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0); +// break; +// case AudioManager.RINGER_MODE_VIBRATE: +// am.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0); +// break; +// case AudioManager.RINGER_MODE_NORMAL: +// am.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolumn, 0); +// break; +// } +// } +} diff --git a/utils/src/main/java/com/thefinestartist/wip/AwakeUtil.java b/utils/src/main/java/com/thefinestartist/wip/AwakeUtil.java new file mode 100644 index 0000000..dc78dbb --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/AwakeUtil.java @@ -0,0 +1,52 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist on 2/10/16. + */ +public class AwakeUtil { + +// private static Map wakeLocks = new HashMap<>(); +// private static final String TAG = "AwakeUtil"; +// +// public static void awakeCPU() { +// awakeCPU(TAG); +// } +// +// public static void awakeCPU(@NonNull String tag) { +// PowerManager.WakeLock wakeLock = ServiceUtil.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, tag); +// wakeLock.acquire(); +// +// if (wakeLocks.get(tag) != null) +// releaseCPU(tag); +// wakeLocks.put(tag, wakeLock); +// } +// +// public static void releaseCPU() { +// releaseCPU(TAG); +// } +// +// public static void releaseCPU(@NonNull String tag) { +// if (wakeLocks.get(tag) == null) +// return; +// +// wakeLocks.get(tag).release(); +// wakeLocks.remove(tag); +// } +// +// public static void awakeScreen(@NonNull Activity activity) { +// activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); +// } +// +// public static void releaseScreen(@NonNull Activity activity) { +// activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); +// } +// +// public void turnOnScreen() { +// ServiceUtil.getPowerManager().newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "tag").acquire(); +// } +// +// public void turnOffScreen() { +// if (APILevel.require(21)) +// ServiceUtil.getPowerManager().newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "tag").acquire(); +// } +} diff --git a/utils/src/main/java/com/thefinestartist/wip/BitmapUtil.java b/utils/src/main/java/com/thefinestartist/wip/BitmapUtil.java new file mode 100644 index 0000000..985003e --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/BitmapUtil.java @@ -0,0 +1,213 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist on 2/9/16. + */ +public class BitmapUtil { + +// private final static String TAG = ImageUtils.class.getSimpleName(); +// +// private static final String ERROR_URI_NULL = "Uri cannot be null"; +// +// /*** +// * Scales the image depending upon the display density of the device. Maintains image aspect +// * ratio. +// * +// * When dealing with the bitmaps of bigger size, this method must be called from a non-UI +// * thread. +// * ***/ +// public static Bitmap scaleDownBitmap(Context ctx, Bitmap source, int newHeight) { +// final float densityMultiplier = Utils.getDensityMultiplier(ctx); +// +// // Log.v( TAG, "#scaleDownBitmap Original w: " + source.getWidth() + " h: " + +// // source.getHeight() ); +// +// int h = (int) (newHeight * densityMultiplier); +// int w = (int) (h * source.getWidth() / ((double) source.getHeight())); +// +// // Log.v( TAG, "#scaleDownBitmap Computed w: " + w + " h: " + h ); +// +// Bitmap photo = Bitmap.createScaledBitmap(source, w, h, true); +// +// // Log.v( TAG, "#scaleDownBitmap Final w: " + w + " h: " + h ); +// +// return photo; +// } +// +// /*** +// * Scales the image independently of the screen density of the device. Maintains image aspect +// * ratio. +// * +// * When dealing with the bitmaps of bigger size, this method must be called from a non-UI +// * thread. +// * ***/ +// public static Bitmap scaleBitmap(Context ctx, Bitmap source, int newHeight) { +// +// // Log.v( TAG, "#scaleDownBitmap Original w: " + source.getWidth() + " h: " + +// // source.getHeight() ); +// +// int w = (int) (newHeight * source.getWidth() / ((double) source.getHeight())); +// +// // Log.v( TAG, "#scaleDownBitmap Computed w: " + w + " h: " + newHeight ); +// +// Bitmap photo = Bitmap.createScaledBitmap(source, w, newHeight, true); +// +// // Log.v( TAG, "#scaleDownBitmap Final w: " + w + " h: " + newHeight ); +// +// return photo; +// } +// +// /*** +// * Scales the image independently of the screen density of the device. Maintains image aspect +// * ratio. +// * +// * @param uri +// * Uri of the source bitmap +// ****/ +// public static Bitmap scaleDownBitmap(Context ctx, Uri uri, int newHeight) throws FileNotFoundException, IOException { +// Bitmap original = Media.getBitmap(ctx.getContentResolver(), uri); +// return scaleBitmap(ctx, original, newHeight); +// } +// +// /*** +// * Scales the image independently of the screen density of the device. Maintains image aspect +// * ratio. +// * +// * @param uri +// * Uri of the source bitmap +// ****/ +// public static Uri scaleDownBitmapForUri(Context ctx, Uri uri, int newHeight) throws FileNotFoundException, IOException { +// +// if (uri == null) +// throw new NullPointerException(ERROR_URI_NULL); +// +// if (!isMediaContentUri(uri)) +// return null; +// +// Bitmap original = Media.getBitmap(ctx.getContentResolver(), uri); +// Bitmap bmp = scaleBitmap(ctx, original, newHeight); +// +// Uri destUri = null; +// String uriStr = Utils.writeImageToMedia(ctx, bmp, "", ""); +// +// if (uriStr != null) { +// destUri = Uri.parse(uriStr); +// } +// +// return destUri; +// } +// +// /*** +// * Gets the orientation of the image pointed to by the parameter uri +// * +// * @return Image orientation value corresponding to ExifInterface.ORIENTATION_*
+// * Returns -1 if the row for the {@link android.net.Uri} is not found. +// ****/ +// public static int getOrientation(Context context, Uri uri) { +// +// int invalidOrientation = -1; +// if (uri == null) { +// throw new NullPointerException(ERROR_URI_NULL); +// } +// +// if (!isMediaContentUri(uri)) { +// return invalidOrientation; +// } +// +// String filePath = Utils.getImagePathForUri(context, uri); +// ExifInterface exif = null; +// +// try { +// exif = new ExifInterface(filePath); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// +// int orientation = invalidOrientation; +// if (exif != null) { +// orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, invalidOrientation); +// } +// +// return orientation; +// } +// +// /*** +// * @deprecated Use {@link MediaUtils#isMediaContentUri(android.net.Uri)} instead.
+// * Checks if the parameter {@link android.net.Uri} is a +// * {@link android.provider.MediaStore.Audio.Media} content uri. +// ****/ +// public static boolean isMediaContentUri(Uri uri) { +// if (!uri.toString().contains("content://media/")) { +// Log.w(TAG, "#isContentUri The uri is not a media content uri"); +// return false; +// } else { +// return true; +// } +// } +// +// /*** +// * Rotate the image at the specified uri. For the rotation of the image the +// * {@link android.media.ExifInterface} data in the image will be used. +// * +// * @param uri +// * Uri of the image to be rotated. +// ****/ +// public static Uri rotateImage(Context context, Uri uri) throws FileNotFoundException, IOException { +// // rotate the image +// if (uri == null) { +// throw new NullPointerException(ERROR_URI_NULL); +// } +// +// if (!isMediaContentUri(uri)) { +// return null; +// } +// +// int invalidOrientation = -1; +// byte[] data = Utils.getMediaData(context, uri); +// +// int orientation = getOrientation(context, uri); +// +// Uri newUri = null; +// +// try { +// +// Log.d(TAG, "#rotateImage Exif orientation: " + orientation); +// +// if (orientation != invalidOrientation) { +// Matrix matrix = new Matrix(); +// +// switch (orientation) { +// case ExifInterface.ORIENTATION_ROTATE_90: +// matrix.postRotate(90); +// break; +// case ExifInterface.ORIENTATION_ROTATE_180: +// matrix.postRotate(180); +// break; +// case ExifInterface.ORIENTATION_ROTATE_270: +// matrix.postRotate(270); +// break; +// } +// +// // set some options so the memory is manager properly +// BitmapFactory.Options options = new BitmapFactory.Options(); +// // options.inPreferredConfig = Bitmap.Config.RGB_565; // try to enable this if +// // OutOfMem issue still persists +// options.inPurgeable = true; +// options.inInputShareable = true; +// +// Bitmap original = BitmapFactory.decodeByteArray(data, 0, data.length, options); +// Bitmap rotatedBitmap = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true); // rotating +// // bitmap +// String newUrl = Media.insertImage(((Activity) context).getContentResolver(), rotatedBitmap, "", ""); +// +// if (newUrl != null) { +// newUri = Uri.parse(newUrl); +// } +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } +// +// return newUri; +// } +} diff --git a/utils/src/main/java/com/thefinestartist/wip/DateUtil.java b/utils/src/main/java/com/thefinestartist/wip/DateUtil.java new file mode 100644 index 0000000..6c237bb --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/DateUtil.java @@ -0,0 +1,87 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist on 1/26/16. + */ +public class DateUtil { + +// public static String SERVER_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.'000Z'"; +// public static String POST_FORMAT = "yy/MM/dd HH:mm"; +// public static String CLICKER_FORMAT = "yyyy/MM/dd kk:mm"; +// public static String DATE_FORMAT = "yyyy/MM/dd"; +// public static String TIME_FORMAT = "HH:mm"; +// private static SimpleDateFormat server_format = new SimpleDateFormat(SERVER_FORMAT); +// private static SimpleDateFormat post_format = new SimpleDateFormat(POST_FORMAT); +// private static SimpleDateFormat date_format = new SimpleDateFormat(DATE_FORMAT); +// private static SimpleDateFormat time_format = new SimpleDateFormat(TIME_FORMAT); +// +// public static synchronized long getCurrentGMTTimeMillis() { +// final Date currentTime = new Date(); +// final SimpleDateFormat sdf = new SimpleDateFormat(SERVER_FORMAT); +// sdf.setTimeZone(TimeZone.getTimeZone("GMT")); +// String time = sdf.format(currentTime); +// return getTime(time); +// } +// +// public static String getCurrentTimeString() { +// return (String) DateFormat.format(CLICKER_FORMAT, System.currentTimeMillis()); +// } +// +// public static long getTime(String timeStr) { +// Date date; +// try { +// server_format.setTimeZone(TimeZone.getTimeZone("GMT")); +// date = server_format.parse(timeStr); +// return date.getTime(); +// } catch (ParseException e) { +// return 0; +// } +// } +// +// public static Date getDate(String timeStr) { +// Date date; +// try { +// server_format.setTimeZone(TimeZone.getTimeZone("GMT")); +// date = server_format.parse(timeStr); +// return date; +// } catch (ParseException e) { +// return null; +// } +// } +// +// public static String getPostFormatString(String timeStr) { +// Date date; +// try { +// server_format.setTimeZone(TimeZone.getTimeZone("GMT")); +// post_format.setTimeZone(TimeZone.getDefault()); +// date = server_format.parse(timeStr); +// return post_format.format(date); +// } catch (ParseException e) { +// return null; +// } +// } +// +// public static String getDateFormatString(String timeStr) { +// Date date; +// try { +// server_format.setTimeZone(TimeZone.getTimeZone("GMT")); +// date_format.setTimeZone(TimeZone.getDefault()); +// date = server_format.parse(timeStr); +// return date_format.format(date); +// } catch (ParseException e) { +// return null; +// } +// } +// +// public static String getTimeFormatString(String timeStr) { +// Date date; +// try { +// server_format.setTimeZone(TimeZone.getTimeZone("GMT")); +// time_format.setTimeZone(TimeZone.getDefault()); +// date = server_format.parse(timeStr); +// return time_format.format(date); +// } catch (ParseException e) { +// return null; +// } +// } +} diff --git a/utils/src/main/java/com/thefinestartist/wip/EmailUtil.java b/utils/src/main/java/com/thefinestartist/wip/EmailUtil.java new file mode 100755 index 0000000..bc36b3c --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/EmailUtil.java @@ -0,0 +1,20 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist + */ +public class EmailUtil { + +// public static void sendSupportMail(String url) { +// Intent i = new Intent(Intent.ACTION_SEND); +// i.setType("message/rfc822"); +// i.putExtra(Intent.EXTRA_EMAIL, new String[]{url}); +// i.putExtra(Intent.EXTRA_SUBJECT, "[FEEDBACK] Android App (" + Build.VERSION.CODENAME + ")"); +// i.putExtra(Intent.EXTRA_TEXT, ""); +// try { +// Base.getContext().startActivity(Intent.createChooser(i, "Send Feedback")); +// } catch (android.content.ActivityNotFoundException e) { +// e.printStackTrace(); +// } +// } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/wip/FileUtil.java b/utils/src/main/java/com/thefinestartist/wip/FileUtil.java new file mode 100644 index 0000000..7b369bb --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/FileUtil.java @@ -0,0 +1,22 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist on 2/10/16. + */ +public class FileUtil { + +// public static String readJsonFile(String filePath) { +// try { +// String json = null; +// InputStream is = Base.getContext().getAssets().open(filePath); +// int size = is.available(); +// byte[] buffer = new byte[size]; +// is.read(buffer); +// is.close(); +// return new String(buffer, "UTF-8"); +// } catch (IOException e) { +// e.printStackTrace(); +// return null; +// } +// } +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/wip/LanguageDetector.java b/utils/src/main/java/com/thefinestartist/wip/LanguageDetector.java new file mode 100644 index 0000000..c68ee6e --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/LanguageDetector.java @@ -0,0 +1,53 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist on 2014. 9. 2.. + */ +public class LanguageDetector { + +// public static boolean isEnglish(CharSequence charSequence) { +// boolean isEnglish = true; +// for (char c : charSequence.toString().toCharArray()) { +// if (Character.UnicodeBlock.of(c) != Character.UnicodeBlock.BASIC_LATIN) { +// isEnglish = false; +// break; +// } +// } +// +// return isEnglish; +// } +// +// public static boolean hasKorean(CharSequence charSequence) { +// boolean hasKorean = false; +// for (char c : charSequence.toString().toCharArray()) { +// if (Character.UnicodeBlock.of(c) == Character.UnicodeBlock.HANGUL_JAMO +// || Character.UnicodeBlock.of(c) == Character.UnicodeBlock.HANGUL_COMPATIBILITY_JAMO +// || Character.UnicodeBlock.of(c) == Character.UnicodeBlock.HANGUL_SYLLABLES) { +// hasKorean = true; +// break; +// } +// } +// +// return hasKorean; +// } +// +// public static boolean hasJapanese(CharSequence charSequence) { +// boolean hasJapanese = false; +// for (char c : charSequence.toString().toCharArray()) { +// if (Character.UnicodeBlock.of(c) == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS +// || Character.UnicodeBlock.of(c) == Character.UnicodeBlock.HIRAGANA +// || Character.UnicodeBlock.of(c) == Character.UnicodeBlock.KATAKANA +// || Character.UnicodeBlock.of(c) == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS +// || Character.UnicodeBlock.of(c) == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS +// || Character.UnicodeBlock.of(c) == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION) { +// hasJapanese = true; +// break; +// } +// } +// +// return hasJapanese; +// } +// +// public enum Language {Korean, Japanese, English} + +} \ No newline at end of file diff --git a/utils/src/main/java/com/thefinestartist/wip/NetworkUtil.java b/utils/src/main/java/com/thefinestartist/wip/NetworkUtil.java new file mode 100644 index 0000000..5f72f5f --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/NetworkUtil.java @@ -0,0 +1,80 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist on 2/21/16. + */ +public class NetworkUtil { + +// public static NetworkInfo getNetworkInfo() { +// return ServiceUtil.getConnectivityManager().getActiveNetworkInfo(); +// } +// +// public static boolean isConnected() { +// NetworkInfo info = getNetworkInfo(); +// return (info != null && info.isConnected()); +// } +// +// public static boolean isConnectedWifi() { +// NetworkInfo info = getNetworkInfo(); +// return (info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI); +// } +// +// public static boolean isConnectedMobile() { +// NetworkInfo info = getNetworkInfo(); +// return (info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_MOBILE); +// } +// +// public static boolean isConnectedFast() { +// NetworkInfo info = getNetworkInfo(); +// return (info != null && info.isConnected() && isConnectionFast(info.getType(), info.getSubtype())); +// } +// +// public static boolean isConnectionFast(int type, int subType) { +// if (type == ConnectivityManager.TYPE_WIFI) { +// return true; +// } else if (type == ConnectivityManager.TYPE_MOBILE) { +// switch (subType) { +// case TelephonyManager.NETWORK_TYPE_1xRTT: +// return false; // ~ 50-100 kbps +// case TelephonyManager.NETWORK_TYPE_CDMA: +// return false; // ~ 14-64 kbps +// case TelephonyManager.NETWORK_TYPE_EDGE: +// return false; // ~ 50-100 kbps +// case TelephonyManager.NETWORK_TYPE_EVDO_0: +// return true; // ~ 400-1000 kbps +// case TelephonyManager.NETWORK_TYPE_EVDO_A: +// return true; // ~ 600-1400 kbps +// case TelephonyManager.NETWORK_TYPE_GPRS: +// return false; // ~ 100 kbps +// case TelephonyManager.NETWORK_TYPE_HSDPA: +// return true; // ~ 2-14 Mbps +// case TelephonyManager.NETWORK_TYPE_HSPA: +// return true; // ~ 700-1700 kbps +// case TelephonyManager.NETWORK_TYPE_HSUPA: +// return true; // ~ 1-23 Mbps +// case TelephonyManager.NETWORK_TYPE_UMTS: +// return true; // ~ 400-7000 kbps +// /* +// * Above API level 7, make sure to set android:targetSdkVersion +// * to appropriate level to use these +// */ +// case TelephonyManager.NETWORK_TYPE_EHRPD: // API level 11 +// return true; // ~ 1-2 Mbps +// case TelephonyManager.NETWORK_TYPE_EVDO_B: // API level 9 +// return true; // ~ 5 Mbps +// case TelephonyManager.NETWORK_TYPE_HSPAP: // API level 13 +// return true; // ~ 10-20 Mbps +// case TelephonyManager.NETWORK_TYPE_IDEN: // API level 8 +// return false; // ~25 kbps +// case TelephonyManager.NETWORK_TYPE_LTE: // API level 11 +// return true; // ~ 10+ Mbps +// // Unknown +// case TelephonyManager.NETWORK_TYPE_UNKNOWN: +// default: +// return false; +// } +// } else { +// return false; +// } +// } +} diff --git a/utils/src/main/java/com/thefinestartist/wip/PhotoUtil.java b/utils/src/main/java/com/thefinestartist/wip/PhotoUtil.java new file mode 100755 index 0000000..2a47b3d --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/PhotoUtil.java @@ -0,0 +1,126 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist + */ +public class PhotoUtil { + +// public static final int IMAGE_MAX_WIDTH = 800; +// +// public static SoftReference convertToByte(String path) { +// if (path == null) +// return new SoftReference<>(new byte[0]); +// +// BitmapFactory.Options options = new BitmapFactory.Options(); +// int inSampleSize = getInSampleSize(path, IMAGE_MAX_WIDTH * 2); +// options.inSampleSize = inSampleSize; +// Bitmap bitmap = BitmapFactory.decodeFile(path, options); +// +// if (bitmap == null) { +// inSampleSize = getInSampleSize(path, IMAGE_MAX_WIDTH * 2); +// options.inSampleSize = inSampleSize; +// bitmap = BitmapFactory.decodeFile(path, options); +// } +// +// if (bitmap == null) +// return new SoftReference<>(new byte[0]); +// +// // Scale the bitmap +// if (inSampleSize > 1) { +// float height; +// switch (getRotation(path)) { +// case ExifInterface.ORIENTATION_ROTATE_90: +// case ExifInterface.ORIENTATION_ROTATE_270: +// height = ((float) bitmap.getWidth()) * ((float) IMAGE_MAX_WIDTH) / ((float) bitmap.getHeight()); +// bitmap = Bitmap.createScaledBitmap(bitmap, (int) height, IMAGE_MAX_WIDTH, true); +// break; +// case ExifInterface.ORIENTATION_NORMAL: +// case ExifInterface.ORIENTATION_ROTATE_180: +// default: +// height = ((float) bitmap.getHeight()) * ((float) IMAGE_MAX_WIDTH) / ((float) bitmap.getWidth()); +// bitmap = Bitmap.createScaledBitmap(bitmap, IMAGE_MAX_WIDTH, (int) height, true); +// break; +// } +// } +// +// // Rotate the bitmap +// switch (getRotation(path)) { +// case ExifInterface.ORIENTATION_ROTATE_90: +// bitmap = rotateBitmap(bitmap, 90); +// break; +// case ExifInterface.ORIENTATION_ROTATE_270: +// bitmap = rotateBitmap(bitmap, 270); +// break; +// } +// +// byte[] bytes = null; +// try { +// ByteArrayOutputStream stream = new ByteArrayOutputStream(); +// bitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream); +// bytes = stream.toByteArray(); +// stream.close(); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// +// return new SoftReference<>(bytes); +// } +// +// public static Bitmap rotateBitmap(Bitmap bitmap, float angle) { +// Matrix matrix = new Matrix(); +// matrix.postRotate(angle); +// return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); +// } +// +// +// public static int getInSampleSize(String path, int maxWidth) { +// SoftReference bitmap = getBitmap(path); +// int inSampleSize = 1; +// float currentWidth = 0; +// switch (getRotation(path)) { +// case ExifInterface.ORIENTATION_ROTATE_90: +// case ExifInterface.ORIENTATION_ROTATE_270: +// if (bitmap != null && bitmap.get() != null) +// currentWidth = bitmap.get().getHeight(); +// break; +// case ExifInterface.ORIENTATION_NORMAL: +// case ExifInterface.ORIENTATION_ROTATE_180: +// default: +// if (bitmap != null && bitmap.get() != null) +// currentWidth = bitmap.get().getWidth(); +// break; +// } +// +// currentWidth /= (float) maxWidth; +// while (currentWidth > 1) { +// inSampleSize *= 2; +// currentWidth /= 2; +// } +// +// return inSampleSize; +// } +// +// public static SoftReference getBitmap(String path) { +// SoftReference bitmap = null; +// +// try { +// bitmap = new SoftReference<>(BitmapFactory.decodeStream(new FileInputStream(new File(path)))); +// } catch (FileNotFoundException e) { +// e.printStackTrace(); +// } +// +// return bitmap; +// } +// +// public static int getRotation(String path) { +// int rotate = ExifInterface.ORIENTATION_NORMAL; +// try { +// File imageFile = new File(path); +// ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath()); +// rotate = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// return rotate; +// } +} diff --git a/utils/src/main/java/com/thefinestartist/wip/RippleUtil.java b/utils/src/main/java/com/thefinestartist/wip/RippleUtil.java new file mode 100644 index 0000000..02669da --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/RippleUtil.java @@ -0,0 +1,18 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist on 2/21/16. + */ +public class RippleUtil { + +// public static void forceEvent(View view, float x, float y) { +// Drawable background = view.getBackground(); +// if (APILevel.require(21)) { +// if (background instanceof RippleDrawable) { +// RippleDrawable ripple = (RippleDrawable) background; +// ripple.setHotspot(x, y); +// ripple.setVisible(true, true); +// } +// } +// } +} diff --git a/utils/src/main/java/com/thefinestartist/wip/Validator.java b/utils/src/main/java/com/thefinestartist/wip/Validator.java new file mode 100644 index 0000000..428c405 --- /dev/null +++ b/utils/src/main/java/com/thefinestartist/wip/Validator.java @@ -0,0 +1,48 @@ +package com.thefinestartist.wip; + +/** + * Created by TheFinestArtist on 2/18/16. + */ +public class Validator { + +// public static final String SPECIAL_CHARS = "\\p{Cntrl}\\(\\)<>@,;:'\\\\\\\"\\.\\[\\]"; +// public static final String VALID_CHARS = "[^\\s" + SPECIAL_CHARS + "]"; +// public static final String QUOTED_USER = "(\"[^\"]*\")"; +// public static final String WORD = "((" + VALID_CHARS + "|')+|" + QUOTED_USER + ")"; +// +// public static final String EMAIL_REGEX = "^\\s*?(.+)@(.+?)\\s*$"; +// public static final String IP_DOMAIN_REGEX = "^\\[(.*)\\]$"; +// public static final String USER_REGEX = "^\\s*" + WORD + "(\\." + WORD + ")*$"; +// +// public static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX); +// public static final Pattern IP_DOMAIN_PATTERN = Pattern.compile(IP_DOMAIN_REGEX); +// public static final Pattern USER_PATTERN = Pattern.compile(USER_REGEX); +// +// +// // Regular expression strings for hostnames (derived from RFC2396 and RFC 1123) +// +// // RFC2396: domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum +// // Max 63 characters +// public static final String DOMAIN_LABEL_REGEX = "\\p{Alnum}(?>[\\p{Alnum}-]{0,61}\\p{Alnum})?"; +// +// // RFC2396 toplabel = alpha | alpha *( alphanum | "-" ) alphanum +// // Max 63 characters +// public static final String TOP_LABEL_REGEX = "\\p{Alpha}(?>[\\p{Alnum}-]{0,61}\\p{Alnum})?"; +// +// // RFC2396 hostname = *( domainlabel "." ) toplabel [ "." ] +// // Note that the regex currently requires both a domain label and a top level label, whereas +// // the RFC does not. This is because the regex is used to detect if a TLD is present. +// // If the match fails, input is checked against DOMAIN_LABEL_REGEX (hostnameRegex) +// // RFC1123 sec 2.1 allows hostnames to start with a digit +// public static final String DOMAIN_NAME_REGEX = "^(?:" + DOMAIN_LABEL_REGEX + "\\.)+" + "(" + TOP_LABEL_REGEX + ")\\.?$"; +// +// +// public static boolean isEmail(String email) { +// String emailPattern = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; +// Pattern pattern = Pattern.compile(emailPattern); +// Matcher matcher = pattern.matcher(email); +// return matcher.matches(); +// } +} +// https://github.com/throrin19/Android-Validator/tree/master/library/src/com/throrinstudio/android/common/libs/validator/validator +// https://github.com/ragunathjawahar/android-saripaar/tree/master/saripaar/src/main/java/commons/validator/routines \ No newline at end of file diff --git a/utils/src/main/res/anim/accelerate_cubic.xml b/utils/src/main/res/anim/accelerate_cubic.xml new file mode 100644 index 0000000..0e5bc09 --- /dev/null +++ b/utils/src/main/res/anim/accelerate_cubic.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/utils/src/main/res/anim/accelerate_quart.xml b/utils/src/main/res/anim/accelerate_quart.xml new file mode 100644 index 0000000..a7bbc3a --- /dev/null +++ b/utils/src/main/res/anim/accelerate_quart.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/utils/src/main/res/anim/accelerate_quint.xml b/utils/src/main/res/anim/accelerate_quint.xml new file mode 100644 index 0000000..2f712c2 --- /dev/null +++ b/utils/src/main/res/anim/accelerate_quint.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/utils/src/main/res/anim/activity_close_enter.xml b/utils/src/main/res/anim/activity_close_enter.xml new file mode 100644 index 0000000..61e042c --- /dev/null +++ b/utils/src/main/res/anim/activity_close_enter.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/activity_close_exit.xml b/utils/src/main/res/anim/activity_close_exit.xml new file mode 100644 index 0000000..2f4f347 --- /dev/null +++ b/utils/src/main/res/anim/activity_close_exit.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/activity_open_enter.xml b/utils/src/main/res/anim/activity_open_enter.xml new file mode 100644 index 0000000..10a8cbb --- /dev/null +++ b/utils/src/main/res/anim/activity_open_enter.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/activity_open_exit.xml b/utils/src/main/res/anim/activity_open_exit.xml new file mode 100644 index 0000000..c9e306a --- /dev/null +++ b/utils/src/main/res/anim/activity_open_exit.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/decelerate_cubic.xml b/utils/src/main/res/anim/decelerate_cubic.xml new file mode 100644 index 0000000..58432b6 --- /dev/null +++ b/utils/src/main/res/anim/decelerate_cubic.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/utils/src/main/res/anim/decelerate_quart.xml b/utils/src/main/res/anim/decelerate_quart.xml new file mode 100644 index 0000000..a3208eb --- /dev/null +++ b/utils/src/main/res/anim/decelerate_quart.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/utils/src/main/res/anim/decelerate_quint.xml b/utils/src/main/res/anim/decelerate_quint.xml new file mode 100644 index 0000000..45b308b --- /dev/null +++ b/utils/src/main/res/anim/decelerate_quint.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/utils/src/main/res/anim/fade_in.xml b/utils/src/main/res/anim/fade_in.xml new file mode 100644 index 0000000..2b50377 --- /dev/null +++ b/utils/src/main/res/anim/fade_in.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/fade_in_fast.xml b/utils/src/main/res/anim/fade_in_fast.xml new file mode 100644 index 0000000..980157e --- /dev/null +++ b/utils/src/main/res/anim/fade_in_fast.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/fade_out.xml b/utils/src/main/res/anim/fade_out.xml new file mode 100644 index 0000000..6fc7e71 --- /dev/null +++ b/utils/src/main/res/anim/fade_out.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/fade_out_fast.xml b/utils/src/main/res/anim/fade_out_fast.xml new file mode 100644 index 0000000..fc1e1af --- /dev/null +++ b/utils/src/main/res/anim/fade_out_fast.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/fade_out_slow.xml b/utils/src/main/res/anim/fade_out_slow.xml new file mode 100644 index 0000000..ae77837 --- /dev/null +++ b/utils/src/main/res/anim/fade_out_slow.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/fragment_close_exit.xml b/utils/src/main/res/anim/fragment_close_exit.xml new file mode 100644 index 0000000..8edf6a1 --- /dev/null +++ b/utils/src/main/res/anim/fragment_close_exit.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/fragment_close_exit_reverse.xml b/utils/src/main/res/anim/fragment_close_exit_reverse.xml new file mode 100644 index 0000000..4bcdcfa --- /dev/null +++ b/utils/src/main/res/anim/fragment_close_exit_reverse.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/fragment_open_enter.xml b/utils/src/main/res/anim/fragment_open_enter.xml new file mode 100644 index 0000000..1c71572 --- /dev/null +++ b/utils/src/main/res/anim/fragment_open_enter.xml @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/fragment_open_enter_reverse.xml b/utils/src/main/res/anim/fragment_open_enter_reverse.xml new file mode 100644 index 0000000..beb6167 --- /dev/null +++ b/utils/src/main/res/anim/fragment_open_enter_reverse.xml @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/modal_activity_close_enter.xml b/utils/src/main/res/anim/modal_activity_close_enter.xml new file mode 100644 index 0000000..61e042c --- /dev/null +++ b/utils/src/main/res/anim/modal_activity_close_enter.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/modal_activity_close_exit.xml b/utils/src/main/res/anim/modal_activity_close_exit.xml new file mode 100644 index 0000000..099e4a3 --- /dev/null +++ b/utils/src/main/res/anim/modal_activity_close_exit.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/modal_activity_open_enter.xml b/utils/src/main/res/anim/modal_activity_open_enter.xml new file mode 100644 index 0000000..7e8c40c --- /dev/null +++ b/utils/src/main/res/anim/modal_activity_open_enter.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/modal_activity_open_exit.xml b/utils/src/main/res/anim/modal_activity_open_exit.xml new file mode 100644 index 0000000..c9e306a --- /dev/null +++ b/utils/src/main/res/anim/modal_activity_open_exit.xml @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/no_anim.xml b/utils/src/main/res/anim/no_anim.xml new file mode 100644 index 0000000..64fb0f1 --- /dev/null +++ b/utils/src/main/res/anim/no_anim.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/splash_out.xml b/utils/src/main/res/anim/splash_out.xml new file mode 100644 index 0000000..25829dc --- /dev/null +++ b/utils/src/main/res/anim/splash_out.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/view_blink.xml b/utils/src/main/res/anim/view_blink.xml new file mode 100644 index 0000000..e0dc143 --- /dev/null +++ b/utils/src/main/res/anim/view_blink.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/view_bounce.xml b/utils/src/main/res/anim/view_bounce.xml new file mode 100644 index 0000000..60ef909 --- /dev/null +++ b/utils/src/main/res/anim/view_bounce.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/view_fade_in.xml b/utils/src/main/res/anim/view_fade_in.xml new file mode 100644 index 0000000..4e7fb83 --- /dev/null +++ b/utils/src/main/res/anim/view_fade_in.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/view_fade_out.xml b/utils/src/main/res/anim/view_fade_out.xml new file mode 100644 index 0000000..4ef8fd1 --- /dev/null +++ b/utils/src/main/res/anim/view_fade_out.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/view_move.xml b/utils/src/main/res/anim/view_move.xml new file mode 100644 index 0000000..046ba1b --- /dev/null +++ b/utils/src/main/res/anim/view_move.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/view_rotate.xml b/utils/src/main/res/anim/view_rotate.xml new file mode 100644 index 0000000..b446a9e --- /dev/null +++ b/utils/src/main/res/anim/view_rotate.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/view_slide_down.xml b/utils/src/main/res/anim/view_slide_down.xml new file mode 100644 index 0000000..00b5b53 --- /dev/null +++ b/utils/src/main/res/anim/view_slide_down.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/view_slide_up.xml b/utils/src/main/res/anim/view_slide_up.xml new file mode 100644 index 0000000..d62e102 --- /dev/null +++ b/utils/src/main/res/anim/view_slide_up.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/view_zoom_in.xml b/utils/src/main/res/anim/view_zoom_in.xml new file mode 100644 index 0000000..20db769 --- /dev/null +++ b/utils/src/main/res/anim/view_zoom_in.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/utils/src/main/res/anim/view_zoom_out.xml b/utils/src/main/res/anim/view_zoom_out.xml new file mode 100644 index 0000000..812c168 --- /dev/null +++ b/utils/src/main/res/anim/view_zoom_out.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/utils/src/main/res/animator/card_flip_left_in.xml b/utils/src/main/res/animator/card_flip_left_in.xml new file mode 100644 index 0000000..5f7027c --- /dev/null +++ b/utils/src/main/res/animator/card_flip_left_in.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + diff --git a/utils/src/main/res/animator/card_flip_left_out.xml b/utils/src/main/res/animator/card_flip_left_out.xml new file mode 100644 index 0000000..a684429 --- /dev/null +++ b/utils/src/main/res/animator/card_flip_left_out.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + diff --git a/utils/src/main/res/animator/card_flip_right_in.xml b/utils/src/main/res/animator/card_flip_right_in.xml new file mode 100644 index 0000000..8089b93 --- /dev/null +++ b/utils/src/main/res/animator/card_flip_right_in.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + diff --git a/utils/src/main/res/animator/card_flip_right_out.xml b/utils/src/main/res/animator/card_flip_right_out.xml new file mode 100644 index 0000000..bdb958e --- /dev/null +++ b/utils/src/main/res/animator/card_flip_right_out.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + diff --git a/utils/src/main/res/values/colors_basic.xml b/utils/src/main/res/values/colors_basic.xml new file mode 100755 index 0000000..7554f19 --- /dev/null +++ b/utils/src/main/res/values/colors_basic.xml @@ -0,0 +1,145 @@ + + + + #FFFFFF + #FFFFF0 + #FFFFE0 + #FFFF00 + #FFFAFA + #FFFAF0 + #FFFACD + #FFF8DC + #FFF5EE + #FFF0F5 + #FFEFD5 + #FFEBCD + #FFE4E1 + #FFE4C4 + #FFE4B5 + #FFDEAD + #FFDAB9 + #FFD700 + #FFC0CB + #FFB6C1 + #FFA500 + #FFA07A + #FF8C00 + #FF7F50 + #FF69B4 + #FF6347 + #FF4500 + #FF1493 + #FF00FF + #FF00FF + #FF0000 + #FDF5E6 + #FAFAD2 + #FAF0E6 + #FAEBD7 + #FA8072 + #F8F8FF + #F5FFFA + #F5F5F5 + #F5F5DC + #F5DEB3 + #F4A460 + #F0FFFF + #F0FFF0 + #F0F8FF + #F0E68C + #F08080 + #EEE8AA + #EE82EE + #E9967A + #E6E6FA + #E0FFFF + #DEB887 + #DDA0DD + #DCDCDC + #DC143C + #DB7093 + #DAA520 + #DA70D6 + #D8BFD8 + #D3D3D3 + #D2B48C + #D2691E + #CD853F + #CD5C5C + #C71585 + #C0C0C0 + #BDB76B + #BC8F8F + #BA55D3 + #B8860B + #B22222 + #B0E0E6 + #B0C4DE + #AFEEEE + #ADFF2F + #ADD8E6 + #A9A9A9 + #A52A2A + #A0522D + #9ACD32 + #9932CC + #98FB98 + #9400D3 + #9370DB + #90EE90 + #8FBC8F + #8B4513 + #8B008B + #8B0000 + #8A2BE2 + #87CEFA + #87CEEB + #808080 + #808000 + #800080 + #800000 + #7FFFD4 + #7FFF00 + #7CFC00 + #7B68EE + #778899 + #708090 + #6B8E23 + #6A5ACD + #696969 + #66CDAA + #6495ED + #5F9EA0 + #556B2F + #4B0082 + #48D1CC + #483D8B + #4682B4 + #4169E1 + #40E0D0 + #3CB371 + #32CD32 + #2F4F4F + #2E8B57 + #228B22 + #20B2AA + #1E90FF + #191970 + #00FFFF + #00FFFF + #00FF7F + #00FF00 + #00FA9A + #00CED1 + #00BFFF + #008B8B + #008080 + #008000 + #006400 + #0000FF + #0000CD + #00008B + #000080 + #000000 + + \ No newline at end of file diff --git a/utils/src/main/res/values/colors_grey.xml b/utils/src/main/res/values/colors_grey.xml new file mode 100755 index 0000000..9286c7f --- /dev/null +++ b/utils/src/main/res/values/colors_grey.xml @@ -0,0 +1,315 @@ + + + + + + #0D0D0D + #1A1A1A + #262626 + #333333 + #404040 + #4D4D4D + #595959 + #666666 + #737373 + #808080 + #8C8C8C + #999999 + #A6A6A5 + #B3B3B3 + #BFBFBF + #CCCCCC + #D9D9D9 + #E6E6E6 + #F2F2F2 + + + #000000 + #010101 + #020202 + #030303 + #040404 + #050505 + #060606 + #070707 + #080808 + #090909 + #0a0a0a + #0b0b0b + #0c0c0c + #0d0d0d + #0e0e0e + #0f0f0f + + #101010 + #111111 + #121212 + #131313 + #141414 + #151515 + #161616 + #171717 + #181818 + #191919 + #1a1a1a + #1b1b1b + #1c1c1c + #1d1d1d + #1e1e1e + #1f1f1f + + #202020 + #212121 + #222222 + #232323 + #242424 + #252525 + #262626 + #272727 + #282828 + #292929 + #2a2a2a + #2b2b2b + #2c2c2c + #2d2d2d + #2e2e2e + #2f2f2f + + #303030 + #313131 + #323232 + #333333 + #343434 + #353535 + #363636 + #373737 + #383838 + #393939 + #3a3a3a + #3b3b3b + #3c3c3c + #3d3d3d + #3e3e3e + #3f3f3f + + #404040 + #414141 + #424242 + #434343 + #444444 + #454545 + #464646 + #474747 + #484848 + #494949 + #4a4a4a + #4b4b4b + #4c4c4c + #4d4d4d + #4e4e4e + #4f4f4f + + #505050 + #515151 + #525252 + #535353 + #545454 + #555555 + #565656 + #575757 + #585858 + #595959 + #5a5a5a + #5b5b5b + #5c5c5c + #5d5d5d + #5e5e5e + #5f5f5f + + #606060 + #616161 + #626262 + #636363 + #646464 + #656565 + #666666 + #676767 + #686868 + #696969 + #6a6a6a + #6b6b6b + #6c6c6c + #6d6d6d + #6e6e6e + #6f6f6f + + #707070 + #717171 + #727272 + #737373 + #747474 + #757575 + #767676 + #777777 + #787878 + #797979 + #7a7a7a + #7b7b7b + #7c7c7c + #7d7d7d + #7e7e7e + #7f7f7f + + #808080 + #818181 + #828282 + #838383 + #848484 + #858585 + #868686 + #878787 + #888888 + #898989 + #8a8a8a + #8b8b8b + #8c8c8c + #8d8d8d + #8e8e8e + #8f8f8f + + #909090 + #919191 + #929292 + #939393 + #949494 + #959595 + #969696 + #979797 + #989898 + #999999 + #9a9a9a + #9b9b9b + #9c9c9c + #9d9d9d + #9e9e9e + #9f9f9f + + #a0a0a0 + #a1a1a1 + #a2a2a2 + #a3a3a3 + #a4a4a4 + #a5a5a5 + #a6a6a6 + #a7a7a7 + #a8a8a8 + #a9a9a9 + #aaaaaa + #ababab + #acacac + #adadad + #aeaeae + #afafaf + + #b0b0b0 + #b1b1b1 + #b2b2b2 + #b3b3b3 + #b4b4b4 + #b5b5b5 + #b6b6b6 + #b7b7b7 + #b8b8b8 + #b9b9b9 + #bababa + #bbbbbb + #bcbcbc + #bdbdbd + #bebebe + #bfbfbf + + #c0c0c0 + #c1c1c1 + #c2c2c2 + #c3c3c3 + #c4c4c4 + #c5c5c5 + #c6c6c6 + #c7c7c7 + #c8c8c8 + #c9c9c9 + #cacaca + #cbcbcb + #cccccc + #cdcdcd + #cecece + #cfcfcf + + #d0d0d0 + #d1d1d1 + #d2d2d2 + #d3d3d3 + #d4d4d4 + #d5d5d5 + #d6d6d6 + #d7d7d7 + #d8d8d8 + #d9d9d9 + #dadada + #dbdbdb + #dcdcdc + #dddddd + #dedede + #dfdfdf + + #e0e0e0 + #e1e1e1 + #e2e2e2 + #e3e3e3 + #e4e4e4 + #e5e5e5 + #e6e6e6 + #e7e7e7 + #e8e8e8 + #e9e9e9 + #eaeaea + #ebebeb + #ececec + #ededed + #eeeeee + #efefef + + #f0f0f0 + #f1f1f1 + #f2f2f2 + #f3f3f3 + #f4f4f4 + #f5f5f5 + #f6f6f6 + #f7f7f7 + #f8f8f8 + #f9f9f9 + #fafafa + #fbfbfb + #fcfcfc + #fdfdfd + #fefefe + #ffffff + + \ No newline at end of file diff --git a/utils/src/main/res/values/colors_transparent.xml b/utils/src/main/res/values/colors_transparent.xml new file mode 100755 index 0000000..9ebb8c4 --- /dev/null +++ b/utils/src/main/res/values/colors_transparent.xml @@ -0,0 +1,91 @@ + + + + + + #11000000 + #22000000 + #33000000 + #44000000 + #55000000 + #66000000 + #77000000 + #88000000 + #99000000 + #aa000000 + #bb000000 + #cc000000 + #dd000000 + #ee000000 + #0D000000 + #1A000000 + #26000000 + #33000000 + #40000000 + #4D000000 + #59000000 + #66000000 + #73000000 + #80000000 + #8C000000 + #99000000 + #A6000000 + #B3000000 + #BF000000 + #CC000000 + #D9000000 + #E6000000 + #F2000000 + + + #11ffffff + #22ffffff + #33ffffff + #44ffffff + #55ffffff + #66ffffff + #77ffffff + #88ffffff + #99ffffff + #aaffffff + #bbffffff + #ccffffff + #ddffffff + #eeffffff + #0Dffffff + #1Affffff + #26ffffff + #33ffffff + #40ffffff + #4Dffffff + #59ffffff + #66ffffff + #73ffffff + #80ffffff + #8Cffffff + #99ffffff + #A6ffffff + #B3ffffff + #BFffffff + #CCffffff + #D9ffffff + #E6ffffff + #F2ffffff + + \ No newline at end of file diff --git a/utils/src/test/java/com/thefinestartist/helpers/ExampleUnitTest.java b/utils/src/test/java/com/thefinestartist/helpers/ExampleUnitTest.java new file mode 100644 index 0000000..99a0022 --- /dev/null +++ b/utils/src/test/java/com/thefinestartist/helpers/ExampleUnitTest.java @@ -0,0 +1,15 @@ +package com.thefinestartist.utils; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * To work on unit tests, switch the Test Artifact in the Build Variants view. + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file