This commit is contained in:
lunaticbum 2024-09-10 17:40:52 +09:00
parent e250a06d39
commit 5929aee345
24 changed files with 864 additions and 263 deletions

View File

@ -34,6 +34,10 @@
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<queries>
<intent>
@ -64,7 +68,6 @@
android:name=".LauncherActivity"
android:theme="@style/Theme.LunarLauncher.Starting"
android:launchMode="singleInstance"
android:screenOrientation="portrait"
android:excludeFromRecents="true"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|screenLayout|layoutDirection"
android:windowSoftInputMode="adjustResize"
@ -151,5 +154,7 @@
<action android:name="android.provider.Telephony.MMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@ -0,0 +1,168 @@
package android.print;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class PDFPrint {
public static void generatePDFFromHTML(final Context context, final File file, final String htmlString, final OnPDFPrintListener onPDFPrintListener) {
final WebView mWebView = new WebView(context);
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
PrintAttributes printAttributes = new PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A4)
.setResolution(new PrintAttributes.Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS)
.build();
final PrintDocumentAdapter documentAdapter = mWebView.createPrintDocumentAdapter(file.getName());
documentAdapter.onLayout(null, printAttributes, null, new PrintDocumentAdapter.LayoutResultCallback() {
@Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
documentAdapter.onWrite(new PageRange[]{PageRange.ALL_PAGES}, getOutputFile(file), null, new PrintDocumentAdapter.WriteResultCallback() {
@Override
public void onWriteCancelled() {
super.onWriteCancelled();
onPDFPrintListener.onError(new Exception("PDF Write cancelled."));
}
@Override
public void onWriteFailed(CharSequence error) {
super.onWriteFailed(error);
onPDFPrintListener.onError(new Exception(error.toString()));
}
@Override
public void onWriteFinished(PageRange[] pages) {
super.onWriteFinished(pages);
onPDFPrintListener.onSuccess(file);
}
});
}
}, null);
}
});
mWebView.loadData(htmlString.replaceAll("#", "%23"), "text/HTML", "UTF-8");
}
public static void generatePDFFromWebView(final File file, final WebView webView, final OnPDFPrintListener onPDFPrintListener) {
PrintAttributes printAttributes = new PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A4)
.setResolution(new PrintAttributes.Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS)
.build();
final PrintDocumentAdapter documentAdapter = webView.createPrintDocumentAdapter(file.getName());
documentAdapter.onLayout(null, printAttributes, null, new PrintDocumentAdapter.LayoutResultCallback() {
@Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
documentAdapter.onWrite(new PageRange[]{PageRange.ALL_PAGES}, getOutputFile(file), null, new PrintDocumentAdapter.WriteResultCallback() {
@Override
public void onWriteCancelled() {
super.onWriteCancelled();
onPDFPrintListener.onError(new Exception("PDF Write cancelled."));
}
@Override
public void onWriteFailed(CharSequence error) {
super.onWriteFailed(error);
try {
if (error != null && error.toString().length() > 0) {
onPDFPrintListener.onError(new Exception(error.toString()));
} else {
onPDFPrintListener.onError(new Exception("Empty Page"));
}
}catch (Exception e) {e.printStackTrace();}
}
@Override
public void onWriteFinished(PageRange[] pages) {
super.onWriteFinished(pages);
onPDFPrintListener.onSuccess(file);
}
});
}
}, null);
}
private static ParcelFileDescriptor getOutputFile(File file) {
try {
if (!file.exists()) {
file.createNewFile();
}
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static PrintJob printPDF(final Activity activity, final File pdfFileToPrint, final PrintAttributes printAttributes) {
PrintManager printManager = (PrintManager) activity.getSystemService(Context.PRINT_SERVICE);
String jobName = Long.valueOf(System.currentTimeMillis()).toString();
return printManager.print(jobName, new PrintDocumentAdapter() {
@Override
public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback) {
InputStream input = null;
OutputStream output = null;
try {
input = new FileInputStream(pdfFileToPrint);
output = new FileOutputStream(destination.getFileDescriptor());
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = input.read(buf)) > 0) {
output.write(buf, 0, bytesRead);
}
callback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
input.close();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras) {
if (cancellationSignal.isCanceled()) {
callback.onLayoutCancelled();
return;
}
PrintDocumentInfo pdi = new PrintDocumentInfo.Builder(pdfFileToPrint.getName()).setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).build();
callback.onLayoutFinished(pdi, true);
}
}, printAttributes);
}
public interface OnPDFPrintListener {
void onSuccess(File file);
void onError(Exception exception);
}
}

View File

@ -35,6 +35,9 @@ import android.net.Uri
import android.net.http.SslError
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Environment.isExternalStorageManager
import android.print.PDFPrint
import android.provider.Settings
import android.telephony.TelephonyManager
import android.view.View
@ -53,12 +56,13 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.lifecycle.ReportFragment.Companion.reportFragment
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import androidx.work.ExistingPeriodicWorkPolicy
@ -109,12 +113,15 @@ import rasel.lunar.launcher.workers.RuliWebGetter
import rasel.lunar.launcher.workers.TheQooGetter
import rasel.lunar.launcher.workers.WorkersDb
import rasel.lunar.launcher.workers.YoutubeGetter
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import kotlin.random.Random
internal class LauncherActivity : AppCompatActivity() {
@ -150,7 +157,7 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<RecentSmsGetter>(longTimePeriod, TimeUnit.MINUTES)
.addTag(SMS_WORK_TAG)
.build())
}, 2, TimeUnit.SECONDS)
}, 1, TimeUnit.SECONDS)
}
fun refreshCalls() {
Executors.newSingleThreadScheduledExecutor().schedule({
@ -161,9 +168,10 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<RecentCallGetter>(longTimePeriod, TimeUnit.MINUTES)
.addTag(CALL_WORK_TAG)
.build())
}, 2, TimeUnit.SECONDS)
}, 1, TimeUnit.SECONDS)
}
fun refreshFeeds() {
var delay = 5L
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(FEDDS_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
@ -171,10 +179,8 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<NewsFeedsGetter>(shortTimePeriod, TimeUnit.MINUTES)
.addTag(FEDDS_WORK_TAG)
.build())
}, 2, TimeUnit.SECONDS)
}
fun refreshYoutube() {
}, delay, TimeUnit.SECONDS)
delay= delay + 5
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(YT_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
@ -182,9 +188,8 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<YoutubeGetter>(longTimePeriod, TimeUnit.MINUTES)
.addTag(YT_WORK_TAG)
.build())
}, 2, TimeUnit.SECONDS)
}
fun refreshReddit() {
}, delay, TimeUnit.SECONDS)
delay= delay + 5
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(REDDIT_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
@ -192,10 +197,8 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<RedditGetter>(midTimePeriod, TimeUnit.MINUTES)
.addTag(REDDIT_WORK_TAG)
.build())
}, 2, TimeUnit.SECONDS)
}
fun refreshComics() {
refreshComics3()
}, delay, TimeUnit.SECONDS)
delay= delay + 5
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(COMIC_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
@ -203,10 +206,8 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<FmKoreaGetter>(midTimePeriod, TimeUnit.MINUTES)
.addTag(COMIC_WORK_TAG)
.build())
refreshComics2()
}, 2, TimeUnit.SECONDS)
}
fun refreshComics2() {
}, delay, TimeUnit.SECONDS)
delay= delay + 5
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(COMIC2_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
@ -214,10 +215,8 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<DotaxGetter>(midTimePeriod, TimeUnit.MINUTES)
.addTag(COMIC2_WORK_TAG)
.build())
}, 2, TimeUnit.SECONDS)
}
fun refreshComics3() {
}, delay, TimeUnit.SECONDS)
delay= delay + 5
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(ClienGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
@ -225,9 +224,8 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<ClienGetter>(midTimePeriod, TimeUnit.MINUTES)
.addTag(ClienGetter.TAG)
.build())
refreshArca()
}, 2, TimeUnit.SECONDS)
}, delay, TimeUnit.SECONDS)
delay= delay + 5
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(DCGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
@ -235,10 +233,8 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<DCGetter>(midTimePeriod, TimeUnit.MINUTES)
.addTag(DCGetter.TAG)
.build())
refreshArca()
}, 2, TimeUnit.SECONDS)
}, delay, TimeUnit.SECONDS)
delay= delay + 5
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(RuliWebGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
@ -246,9 +242,8 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<RuliWebGetter>(midTimePeriod, TimeUnit.MINUTES)
.addTag(RuliWebGetter.TAG)
.build())
refreshArca()
}, 2, TimeUnit.SECONDS)
}, delay, TimeUnit.SECONDS)
delay= delay + 5
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(TheQooGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
@ -256,12 +251,8 @@ internal class LauncherActivity : AppCompatActivity() {
PeriodicWorkRequestBuilder<TheQooGetter>(midTimePeriod, TimeUnit.MINUTES)
.addTag(TheQooGetter.TAG)
.build())
refreshArca()
}, 2, TimeUnit.SECONDS)
}
fun refreshArca() {
}, delay, TimeUnit.SECONDS)
delay= delay + 5
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(ArcaGetter.TAG)
mWorkManager?.enqueueUniquePeriodicWork(
@ -270,7 +261,7 @@ internal class LauncherActivity : AppCompatActivity() {
.addTag(ArcaGetter.TAG)
.build())
}, 2, TimeUnit.SECONDS)
}, delay, TimeUnit.SECONDS)
}
fun workmanager() : WorkManager? {
@ -337,14 +328,7 @@ internal class LauncherActivity : AppCompatActivity() {
refreshSms()
refreshCalls()
refreshFeeds()
refreshYoutube()
refreshReddit()
refreshComics()
// if (listItem.size < 2) {
// lActivity?.doWebParseStart(jGuruRanks) {
//
// }
// }
}
override fun onDestroy() {
@ -370,7 +354,20 @@ internal class LauncherActivity : AppCompatActivity() {
val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
startActivity(intent)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!isExternalStorageManager()) {
try {
startActivityForResult(Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply {
addCategory("android.intent.category.DEFAULT")
data = Uri.parse(String.format("package:%s", applicationContext.packageName))
}, 300)
} catch (e: Exception) {
startActivityForResult(Intent().apply {
action = Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
}, 300)
}
}
}
}
private fun welcomeDialog() {
@ -536,6 +533,146 @@ internal class LauncherActivity : AppCompatActivity() {
}
}
fun openDrive(gmmIntentUri : Uri, pakage : String? = null) {
val mapIntent = Intent(Intent.ACTION_MEDIA_SHARED, gmmIntentUri)
pakage?.let {
mapIntent.setPackage(pakage)
}
startActivity(mapIntent)
}
fun doWebSavor(url : String, callBack :CommadCallabck?) {
if (true)return
this.callBack = callBack
binding.searcher01.post { binding.searcher01.visibility = View.VISIBLE }
BLog.LOGE("binding.otherCheck before ThreadRun")
binding.searcher01.bringToFront()
binding.searcher01.alpha = 1f
binding.searcher01.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
if (url?.contains("missav") == true && isF) {
BLog.LOGE("binding.otherCheck before reload")
view?.loadUrl(url!!)
isF = true
return false
}
return false
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
BLog.LOGE("binding.otherCheck searcher01 in onPageStarted ${url}")
super.onPageStarted(view, url, favicon)
}
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest?,
error: WebResourceError?
) {
}
override fun onReceivedSslError(
view: WebView?,
handler: SslErrorHandler?,
error: SslError?
) {
handler?.proceed()
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
autoScrollDown(view,url)
}
}
WebView.setWebContentsDebuggingEnabled(false)
binding.searcher01.apply {
setBackgroundColor(Color.WHITE) // 백그라운드 색상 설정
setLayerType(View.LAYER_TYPE_SOFTWARE, null) // 랜더링 이슈 해결
try {
settings.apply {
userAgentString = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"
javaScriptEnabled = true // 자바스크립트 사용 가능하도록 설정
loadWithOverviewMode = true // 전체 웹페이지를 화면에 맞게 로드
useWideViewPort = true // 화면에 맞게 페이지 확대/축소
domStorageEnabled = true // DOM 저장소 사용 가능하도록 설정
setSupportMultipleWindows(true)
javaScriptCanOpenWindowsAutomatically = true // 팝업창 차단 해제
cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
textZoom = 100 // system 에 의한 글꼴 변형 방지
defaultTextEncodingName = "UTF-8" // 인코딩 설정
allowContentAccess = true // 웹뷰를 통해 content url에 접근할지 여부
layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING // 웹페이지의 레이아웃을 화면에 맞게 자동으로 조정
}
} catch (ignore: NoSuchMethodError) {
}.apply {
loadUrl(url) // 웹페이지 연결
}
}
}
fun autoScrollDown(webView: WebView?, url: String?) {
webView?.let { webView ->
val ramdomTimeSec =
800L.plus(Math.abs(Random(System.currentTimeMillis()).nextLong().rem(489L)))
BLog.LOGE("ramdomTime >>> ${ramdomTimeSec}")
if (((webView?.scrollY ?: 0) + (webView?.height
?: 0)) < webView?.contentHeight ?: 0
) {
webView?.postDelayed({
webView?.scrollY = (binding.searcher01.scrollY) + (binding.searcher01.height.toFloat() * 0.4).toInt()
autoScrollDown(webView, url)
}, ramdomTimeSec)
} else {
webView?.postDelayed({
binding.viewPager.bringToFront()
binding.searcher01.alpha = 0f
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
val fileName = url?.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,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(
this@LauncherActivity,
"rasel.lunar.launcher.debug.fileprovider", //(use your app signature + ".provider" )
file
)
putExtra(Intent.EXTRA_STREAM, imageUri)
type = "pdf"
}
this@LauncherActivity.startActivity(shareIntent)
}
override fun onError(exception: java.lang.Exception?) {
Toast.makeText(this@LauncherActivity,
"Pdf Save Failk ${exception?.localizedMessage}", Toast.LENGTH_LONG).show()
exception?.printStackTrace()
}
} )
} catch (e: IOException) {
e.printStackTrace()
}
} else {
}
}, ramdomTimeSec)
}
}
}
var callBack : CommadCallabck? = null
var isF = false
fun doWebParseStart(url : String, callBack :CommadCallabck?) {

View File

@ -62,10 +62,7 @@ import rasel.lunar.launcher.CommadCallabck
import rasel.lunar.launcher.LauncherActivity.Companion.appWidgetHost
import rasel.lunar.launcher.LauncherActivity.Companion.appWidgetManager
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.LauncherActivity.Companion.refreshComics
import rasel.lunar.launcher.LauncherActivity.Companion.refreshFeeds
import rasel.lunar.launcher.LauncherActivity.Companion.refreshReddit
import rasel.lunar.launcher.LauncherActivity.Companion.refreshYoutube
import rasel.lunar.launcher.R
import rasel.lunar.launcher.databinding.FeedsBinding
import rasel.lunar.launcher.feeds.rss.RssAdapter
@ -228,6 +225,9 @@ internal class Feeds : Fragment() , CommadCallabck {
if (input.text.toString().trim().contains(" ")) {
val cmd = input.text.toString().trim().split(" ")
when(cmd[0]) {
"s" -> {
home?.queryInfos(keyword = cmd[1])
}
"jf" -> {
consoleLog("on Cmd JF")
GlobalScope.launch { excuteGetterMostByUrl("https://javmost.to/search/movie/${cmd[1]}") }
@ -284,6 +284,9 @@ internal class Feeds : Fragment() , CommadCallabck {
addAll(RssDataType.values())
remove(RssDataType.GURU)
remove(RssDataType.Most)
})
"all" -> home?.queryInfos(arrayListOf<RssDataType>().apply {
})
"onews" -> home?.queryInfos(arrayListOf<RssDataType>().apply {
addAll(RssDataType.values())
@ -307,26 +310,12 @@ internal class Feeds : Fragment() , CommadCallabck {
}
}.start()
}
"ytb" -> {
refreshYoutube()
consoleLog("excute refreshYoutube()")
}
"reddit" -> {
refreshReddit()
consoleLog("excute refreshReddit()")
}
"fedd" -> {
"req" -> {
refreshFeeds()
consoleLog("excute refreshFeeds()")
}
"comic" -> {
refreshComics()
consoleLog("excute refreshComics()")
}
"taxi" -> {
consoleLog("before run State home?.binding?.alcholKatalkT?.isVisible >> ${home?.binding?.alcholKatalkT?.isVisible}")
home?.showAl()

View File

@ -22,6 +22,7 @@ import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
@ -34,10 +35,17 @@ import android.view.LayoutInflater
import android.view.View
import android.view.View.OnScrollChangeListener
import android.view.ViewGroup
import android.widget.EditText
import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.TableRow
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat.RECEIVER_EXPORTED
import androidx.core.content.ContextCompat.registerReceiver
import androidx.core.view.children
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.DividerItemDecoration
@ -45,6 +53,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
import com.google.gson.Gson
import io.realm.kotlin.ext.query
import io.realm.kotlin.notifications.InitialResults
import io.realm.kotlin.notifications.ResultsChange
@ -53,17 +62,17 @@ import io.realm.kotlin.query.RealmResults
import io.realm.kotlin.query.Sort
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.json.JSONArray
import org.json.JSONObject
import org.jsoup.Jsoup
import rasel.lunar.launcher.CommadCallabck
import rasel.lunar.launcher.LauncherActivity.Companion.CALL_WORK_TAG
import rasel.lunar.launcher.LauncherActivity.Companion.SMS_WORK_TAG
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.LauncherActivity.Companion.refreshComics
import rasel.lunar.launcher.LauncherActivity.Companion.refreshFeeds
import rasel.lunar.launcher.LauncherActivity.Companion.refreshReddit
import rasel.lunar.launcher.LauncherActivity.Companion.refreshYoutube
import rasel.lunar.launcher.LauncherActivity.Companion.workmanager
import rasel.lunar.launcher.R
import rasel.lunar.launcher.databinding.LauncherHomeBinding
@ -76,6 +85,7 @@ import rasel.lunar.launcher.helpers.UniUtils.Companion.canAuthenticate
import rasel.lunar.launcher.helpers.UniUtils.Companion.expandNotificationPanel
import rasel.lunar.launcher.helpers.UniUtils.Companion.lockMethod
import rasel.lunar.launcher.home.weather.WeatherExecutor
import rasel.lunar.launcher.model.CiliMagnet
import rasel.lunar.launcher.model.NotificationItem
import rasel.lunar.launcher.model.RssData
import rasel.lunar.launcher.qaccess.QuickAccess
@ -87,12 +97,15 @@ import rasel.lunar.launcher.todos.NotificationItemAdapter
import rasel.lunar.launcher.todos.RssItemAdapter
import rasel.lunar.launcher.todos.SmsLogsAdapter
import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.RssList.jGuruMain
import rasel.lunar.launcher.utils.SimpleFingerGestures
import rasel.lunar.launcher.utils.beforeDay
import rasel.lunar.launcher.view.TableRadioGroup
import rasel.lunar.launcher.workers.RecentCall
import rasel.lunar.launcher.workers.RecentSms
import rasel.lunar.launcher.workers.WorkersDb
import java.net.URLEncoder
import java.nio.charset.Charset
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
@ -143,16 +156,12 @@ internal class LauncherHome : Fragment() {
}
val notiUpdate = Runnable {
if (lastedNoti?.size ?: 0 > 0) {
binding.otherCheck.text = "최근 정보[${lasted!!.size}]"
mRssAdapter.updateData(lasted!!)
// binding.infoList.smoothScrollToPosition(0)
}
chooseAdpater()
}
var lasted : RealmResults<RssData>? = null
var lastedNoti : RealmResults<NotificationItem>? = null
var lasted : List<RssData>? = null
var lastedNoti : List<NotificationItem>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -188,6 +197,8 @@ internal class LauncherHome : Fragment() {
binding.infoList.adapter = mRssAdapter
binding.notiList.adapter = mNotiAdapter
binding.favAppsGroup.setOnClickListener { searchData() }
try{binding.mainList.removeOnScrollListener(onScrChanged)}catch (e : Exception){e.printStackTrace()}
binding.mainList.addOnScrollListener(onScrChanged)
try{binding.smsList.removeOnScrollListener(onScrChanged)}catch (e : Exception){e.printStackTrace()}
@ -265,10 +276,11 @@ internal class LauncherHome : Fragment() {
when (changes) {
is UpdatedResults -> {
BLog.LOGE("ResultsChange onNotificationPosted")
lastedNoti = changes.list
commandHandler.removeCallbacks(infoUpdate)
commandHandler.postDelayed(infoUpdate, UPDATE_DELAY)
WorkersDb.getRealm().apply {
lastedNoti = copyFromRealm(changes.list)
}
commandHandler.removeCallbacks(notiUpdate)
commandHandler.postDelayed(notiUpdate, UPDATE_DELAY)
}
else -> {
@ -277,8 +289,6 @@ internal class LauncherHome : Fragment() {
}
}
}
mNotificationResult?.let { mNotiAdapter?.updateData(it) }
noticeJob?.start()
}
@ -298,7 +308,8 @@ internal class LauncherHome : Fragment() {
filter!!.forEach {
rQ = rQ.query("category != $0", it.name)
}
mRssDataResult = rQ.sort("pubDate ", Sort.DESCENDING).limit(1000).find()
//limit(1000)
mRssDataResult = rQ.sort("pubDate ", Sort.DESCENDING).find()
BLog.LOGE("${this} ::::: queryInfos after query find >>>> ")
infosJob = CoroutineScope(Dispatchers.Default).launch {
mRssDataResult?.asFlow()?.collect { changes: ResultsChange<RssData> ->
@ -307,7 +318,10 @@ internal class LauncherHome : Fragment() {
when (changes) {
is InitialResults -> {
BLog.LOGE("${this} ::::: queryInfos after changes size >>>> ${changes.list.size}")
lasted = changes.list
WorkersDb.getRealm().apply {
lasted = copyFromRealm(changes.list)
}
commandHandler.postDelayed(infoUpdate, UPDATE_DELAY * 3)
}
is UpdatedResults -> {
@ -319,11 +333,54 @@ internal class LauncherHome : Fragment() {
}
}
}
mRssDataResult?.let {
BLog.LOGE("${this} ::::: queryInfos updateData >>>> ")
// lasted = it
// commandHandler.postDelayed(infoUpdate, UPDATE_DELAY * 3)
mRssAdapter.updateData(it)
infosJob?.start()
}
fun queryInfos(keyword : String, category : String? = null) {
BLog.LOGE("${this} ::::: queryInfos >>>> ${keyword}")
try { infosJob?.cancel() } catch (e:Exception) {e.printStackTrace()}
mRssDataResult = null
try {
System.gc()
}catch (e : Exception){e.printStackTrace()}
WorkersDb.getRealm().apply { writeBlocking {
delete(query<RssData>().query("pubDate < $0",beforeDay(Date(),3)).query("category != $0 || category != $1 ", RssDataType.GURU.name,RssDataType.Most.name).find())
}}
BLog.LOGE("${this} ::::: queryInfos after delete >>>> ")
var rQ = WorkersDb.getRealm().query<RssData>().query("pubDate > $0", beforeDay(Date(),3))
if(keyword.length > 0)
keyword.split("").forEach {
rQ = rQ.query("title CONTAINS $0 OR title CONTAINS $1", it.toUpperCase(), it.toLowerCase())
}
category?.let {
rQ = rQ.query("category == $0", it)
}
//limit(1000)
mRssDataResult = rQ.sort("pubDate ", Sort.DESCENDING).find()
BLog.LOGE("${this} ::::: queryInfos after query find >>>> ")
infosJob = CoroutineScope(Dispatchers.Default).launch {
mRssDataResult?.asFlow()?.collect { changes: ResultsChange<RssData> ->
commandHandler.removeCallbacks(infoUpdate)
when (changes) {
is InitialResults -> {
BLog.LOGE("${this} ::::: queryInfos after changes size >>>> ${changes.list.size}")
WorkersDb.getRealm().apply {
lasted = copyFromRealm(changes.list)
}
commandHandler.postDelayed(infoUpdate, UPDATE_DELAY * 3)
}
is UpdatedResults -> {
// lasted = changes.list
// commandHandler.postDelayed(infoUpdate, UPDATE_DELAY * 3)
}
else -> {
}
}
}
}
infosJob?.start()
}
@ -397,6 +454,7 @@ internal class LauncherHome : Fragment() {
binding.otherCheck.isSelected = false
} else {
queryInfos()
binding.otherCheck.isSelected = true
views.remove(binding.infoList)
binding.infoList.scrollToPosition(0)
@ -407,6 +465,7 @@ internal class LauncherHome : Fragment() {
if (binding.notice.isSelected ) {
binding.notice.isSelected = false
} else {
queryNotice()
binding.notice.isSelected = true
views.remove(binding.notiList)
binding.notiList.visibility = View.VISIBLE
@ -426,16 +485,60 @@ internal class LauncherHome : Fragment() {
binding.otherCheck.setOnLongClickListener {
queryInfos()
refreshYoutube()
refreshComics()
refreshFeeds()
refreshReddit()
true
}
}
// https://www.youtube.com/results?search_query=sds
fun searchData() {
val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
builder.setTitle("Command Line")
val viewInflated: View = LayoutInflater.from(context)
.inflate(R.layout.search_layout, view as ViewGroup?, false)
val input = viewInflated.findViewById<View>(R.id.input) as EditText
val categoryz = viewInflated.findViewById<TableRadioGroup>(R.id.categoryz) as TableRadioGroup
categoryz.setMaxColumns(5)
categoryz.setMaxRows(5)
var idx = 0
RssDataType.values().reversed().toList().chunked(5).forEach {
var tb = TableRow(requireContext())
it.forEach { c ->
if(c.equals(RssDataType.NO_DATA) == false) {
tb.addView(RadioButton(requireContext()).apply {
this.tag = c.name
this.text = c.name
})
}
}
categoryz.addView(tb)
}
builder.setView(viewInflated)
builder.setPositiveButton(android.R.string.ok,
DialogInterface.OnClickListener { dialog, which ->
dialog.dismiss()
var category : String? = null
categoryz.children.forEach {
if(it is TableRow) {
it.children.forEach {
if (it is RadioButton && it.isChecked) {
(it.tag as? String)?.let { category = it }
}
}
}
}
queryInfos(keyword =input.text.toString(),category)
})
builder.setNegativeButton(android.R.string.cancel,
DialogInterface.OnClickListener { dialog, which -> dialog.cancel() })
builder.show()
}
fun showAl() {
binding.alcholKatalkT.visibility = View.VISIBLE
binding.alcholKatalkT.setOnClickListener {
@ -465,61 +568,61 @@ internal class LauncherHome : Fragment() {
binding.infoList.visibility = View.INVISIBLE
binding.notiList.visibility = View.INVISIBLE
if (binding.missedCalls.isSelected) {
if (recentCalls.size > 0 && isAdded && isResumed && isVisible) {
if (recentCalls.size > 0) {
try {
callList.clear()
recentCalls.forEach { t, u ->
callList.add(u)
}.apply {
callList.sortByDescending { it.date }
Handler(Looper.getMainLooper()).post {
binding.mainList.visibility = View.VISIBLE
mMissedCallsAdapter.updateData(callList)
binding.recentSms.isSelected = false
binding.otherCheck.isSelected = false
binding.notice.isSelected = false
}
// Handler(Looper.getMainLooper()).post {
binding.mainList.visibility = View.VISIBLE
mMissedCallsAdapter.updateData(callList)
binding.recentSms.isSelected = false
binding.otherCheck.isSelected = false
binding.notice.isSelected = false
// }
}
} catch (e : Exception) {
}
}
} else if(binding.recentSms.isSelected){
if (smsList.size > 0 && isAdded && isResumed && isVisible) {
if (smsList.size > 0) {
try {
smsList.sortByDescending { it.rcvDate }
binding.smsList.visibility = View.VISIBLE
Handler(Looper.getMainLooper()).post {
binding.smsList.visibility = View.VISIBLE
mSmsLogsAdapter.updateData(smsList)
binding.missedCalls.isSelected = false
binding.otherCheck.isSelected = false
binding.notice.isSelected = false
}
// Handler(Looper.getMainLooper()).post {
binding.smsList.visibility = View.VISIBLE
mSmsLogsAdapter.updateData(smsList)
binding.missedCalls.isSelected = false
binding.otherCheck.isSelected = false
binding.notice.isSelected = false
// }
} catch (e : Exception) {
}
}
} else if(binding.otherCheck.isSelected) {
Handler(Looper.getMainLooper()).post {
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) }
// Handler(Looper.getMainLooper()).post {
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) {
Handler(Looper.getMainLooper()).post {
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)}
}
// Handler(Looper.getMainLooper()).post {
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)}
// }
}
commandHandler.postDelayed(hideListView, UPDATE_DELAY * 5)
}

View File

@ -1,6 +1,7 @@
package rasel.lunar.launcher.model
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.Ignore
import io.realm.kotlin.types.annotations.PrimaryKey
import org.jsoup.select.Elements
import rasel.lunar.launcher.utils.BLog
@ -191,9 +192,15 @@ class RssData : RealmObject, RssDataInterface {
var thumbnail : String? = null
var pubDate : Long = 0L
var category : String? = null
@Ignore
var mRssDataType : RssDataType? = null
override fun title(): String {
return title ?: ""
return when(category()){
RssDataType.NewsFeed -> {
if(title?.length ?: 0 > 30) title?.substring(0,30).plus("...") else title ?: ""
}
else -> title ?: ""
}
}
override fun thumbnailUrl(): String {
@ -205,7 +212,16 @@ class RssData : RealmObject, RssDataInterface {
}
override fun description(): String {
return description ?: ""
return when(category()){
RssDataType.YOUTUBE -> {
if(description?.contains("게시자") == true) description!!.split("게시자")[0] else description ?: ""
}
RssDataType.NewsFeed -> {
category().name
}
else -> description.plus(" / ").plus(category().name)
}
}
override fun pubDate(): Long {
@ -213,7 +229,9 @@ class RssData : RealmObject, RssDataInterface {
}
override fun category(): RssDataType {
return RssDataType.valueOf(category!!)
if (mRssDataType == null)
mRssDataType = RssDataType.valueOf(category!!)
return mRssDataType!!
}
}

View File

@ -41,9 +41,98 @@ import java.text.SimpleDateFormat
import java.util.Date
fun openNews(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.setPackage("com.android.chrome")
lActivity?.startActivity(mapIntent)
}
fun openYouTube(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.setPackage("com.google.android.youtube")
lActivity?.startActivity(mapIntent)
}
fun openReddit(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.setPackage("com.reddit.frontpage")
lActivity?.startActivity(mapIntent)
}
fun openDotax(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW)
mapIntent.setPackage("net.daum.android.cafe")
mapIntent.setData(gmmIntentUri)
lActivity?.startActivity(mapIntent)
}
fun openOpera(schemeString : String) {
BLog.LOGE("openOpera ${schemeString}")
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
mapIntent.setPackage("com.opera.browser")
lActivity?.startActivity(mapIntent)
}
internal class RssItemAdapter (
private val context: Context) : RecyclerView.Adapter<RssTag>() {
private var rssDataItemLis: ArrayList<RssDataInterface> = arrayListOf()
companion object {
@SuppressLint("SimpleDateFormat")
val dateFormat = SimpleDateFormat("a HH:mm / yy - MM - dd")
val emptyDate = " - "
val dateViewClick = View.OnClickListener { v ->
(v?.tag as? Int)?.let { idx ->
val rss = rssDataItemLis[idx]
when(rss.category()) {
RssDataType.GURU,RssDataType.Most,RssDataType.REDDIT_nsfw -> {
v.findViewById<ShapeableImageView>(R.id.circle_preview)?.let {
if (it.visibility == View.GONE) {
it.visibility = View.VISIBLE
it.postDelayed({
it.visibility = View.GONE
}, 2000L)
} else {
openOpera(rss.originPage())
}
}
}
RssDataType.REDDIT -> { openReddit(rss.originPage()) }
RssDataType.Dotax -> { openDotax(rss.originPage()) }
RssDataType.YOUTUBE -> { openYouTube(rss.originPage()) }
else -> { openNews(rss.originPage()) }
}
}
}
private var rssDataItemLis: ArrayList<RssDataInterface> = arrayListOf()
val mLongClickListener = View.OnLongClickListener { v ->
(v?.tag as? Int)?.let { idx ->
val rss = rssDataItemLis[idx]
lActivity?.doWebSavor(rss.originPage(),null)
// when (rss.category()) {
// RssDataType.GURU ,RssDataType.Most , RssDataType.TAGS-> { openOpera(rss.originPage()) }
// RssDataType.REDDIT -> { openReddit(rss.originPage()) }
// RssDataType.Dotax -> { openDotax(rss.originPage()) }
// RssDataType.YOUTUBE -> { openYouTube(rss.originPage()) }
// else -> { openNews(rss.originPage()) }
// }
}
true
}
}
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): RssTag {
val binding = ListItemWithBinding.inflate(LayoutInflater.from(viewGroup.context), viewGroup, false)
return RssTag(binding)
@ -53,7 +142,7 @@ internal class RssItemAdapter (
return rssDataItemLis.size
}
val dateFormat = SimpleDateFormat("a HH:mm / yy - MM - dd")
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: RssTag, position: Int) {
@ -62,77 +151,35 @@ internal class RssItemAdapter (
if (rssData.pubDate() > 1000L) {
holder.view.date.text = dateFormat.format(Date(rssData.pubDate()))
} else {
holder.view.date.text = ""
holder.view.date.text = emptyDate
}
holder.view.title.text = rssData.title()
when(rssData.category()) {
RssDataType.YOUTUBE -> {
holder.view.desc.text = if(rssData.description().contains("게시자")) rssData.description().split("게시자")[0] else rssData.description()
}
RssDataType.NewsFeed -> {
holder.view.desc.text = rssData.category().name
holder.view.title.text = if(rssData.title().length > 30)rssData.title().substring(0,30).plus("...") else rssData.title()
}
RssDataType.NO_DATA -> {}
else -> {
holder.view.desc.text = rssData.description().plus(" ").plus(rssData.category().name)
}
}
holder.view.desc.text = rssData.description()
var param = holder.view.circlePreview.layoutParams
holder.view.circlePreview.layoutParams = ConstraintLayout.LayoutParams(rssData.category().defaultImgSize(), param.height)
holder.view.circlePreview.visibility = rssData.category().getDefaultVisibiliy()
Picasso.get().cancelRequest(holder.view.circlePreview)
if(rssData.thumbnailUrl()?.length ?: 0 > 6) {
Picasso.get().load(rssData.thumbnailUrl().replace("&amp;","&").toUri()).into(holder.view.circlePreview)
} else if (rssData.category().getResId() > 0 ) {
holder.view.circlePreview.setImageResource(rssData.category().getResId())
} else {
holder.view.circlePreview.setImageDrawable(null)
Picasso.get().cancelRequest(holder.view.circlePreview)
}
holder.view.root.tag = position
holder.view.root.setOnClickListener(dateViewClick)
holder.view.root.setOnLongClickListener(mLongClickListener)
}
val dateViewClick = View.OnClickListener {
it.findViewById<ShapeableImageView>(R.id.circle_preview)?.let {
if (it.visibility == View.GONE) {
it.visibility = View.VISIBLE
it.postDelayed({
it.visibility = View.GONE
}, 1000L)
}
}
}
val mLongClickListener = View.OnLongClickListener { v ->
(v?.tag as? Int)?.let { idx ->
val rss = rssDataItemLis[idx]
when (rss.category()) {
RssDataType.GURU ,RssDataType.Most , RssDataType.TAGS-> { openOpera(rss.originPage()) }
RssDataType.REDDIT -> { openReddit(rss.originPage()) }
RssDataType.Dotax -> { openDotax(rss.originPage()) }
RssDataType.YOUTUBE -> { openYouTube(rss.originPage()) }
else -> { openNews(rss.originPage()) }
}
}
true
}
fun openOpera(schemeString : String) {
BLog.LOGE("openOpera ${schemeString}")
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
mapIntent.setPackage("com.opera.browser")
lActivity?.startActivity(mapIntent)
}
fun updateData(newList: List<RssDataInterface>) {
try {
BLog.LOGE("newList >> ${newList}")
// BLog.LOGE("newList >> ${newList}")
DiffUtil.calculateDiff(RssItemDiffUtil(rssDataItemLis, newList)).apply {
}.dispatchUpdatesTo(this).apply {
@ -140,49 +187,13 @@ internal class RssItemAdapter (
}
rssDataItemLis.clear()
rssDataItemLis.addAll(newList)
BLog.LOGE("rssDataItemLis >> ${rssDataItemLis}")
}catch ( e : Exception) {
// BLog.LOGE("rssDataItemLis >> ${rssDataItemLis}")
} catch (e: Exception) {
e.printStackTrace()
}
}
fun openNews(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.setPackage("com.android.chrome")
lActivity?.startActivity(mapIntent)
}
fun openYouTube(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.setPackage("com.google.android.youtube")
lActivity?.startActivity(mapIntent)
}
fun openReddit(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.setPackage("com.reddit.frontpage")
lActivity?.startActivity(mapIntent)
}
fun openDotax(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW)
mapIntent.setPackage("net.daum.android.cafe")
mapIntent.setData(gmmIntentUri)
lActivity?.startActivity(mapIntent)
}
}
//fun dp2px(dp: Float): Float {
// val resources: Resources = this.getResources()
// val metrics = resources.displayMetrics
// val px = dp * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
// return px
//}
internal class RssTag(var view: ListItemWithBinding) : RecyclerView.ViewHolder(view.root) {}
internal class RssItemDiffUtil(
var oldList: List<RssDataInterface>, var newList: List<RssDataInterface>

View File

@ -0,0 +1,128 @@
package rasel.lunar.launcher.view
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.View.OnClickListener
import android.view.ViewGroup
import android.widget.RadioButton
import android.widget.TableLayout
import android.widget.TableRow
class TableRadioGroup : TableLayout {
var checkedRadioButtonId: Int = -1
private set
private var onCheckedChangeListener: OnCheckedChangeListener? = null
private var maxColumns = -1 // Maximum number of columns (-1 for unlimited)
private var maxRows = -1 // Maximum number of rows (-1 for unlimited)
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
fun setMaxColumns(maxColumns: Int) {
this.maxColumns = maxColumns
}
fun setMaxRows(maxRows: Int) {
this.maxRows = maxRows
}
override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) {
if (child is TableRow) {
val numChildren = child.childCount
for (i in 0 until numChildren) {
val view = child.getChildAt(i)
if (view is RadioButton) {
setupRadioButton(view)
}
}
}
super.addView(child, index, params)
// Adjust the number of rows and columns if necessary
adjustTableLayout()
}
private fun setupRadioButton(radioButton: RadioButton?) {
if (radioButton == null) {
return
}
radioButton.setOnClickListener(OnClickListener {
if (checkedRadioButtonId != -1) {
setCheckedStateForView(checkedRadioButtonId, false)
}
val id = radioButton.id
setCheckedStateForView(id, true)
checkedRadioButtonId = id
if (onCheckedChangeListener != null) {
onCheckedChangeListener!!.onCheckedChanged(
this@TableRadioGroup,
checkedRadioButtonId
)
}
})
}
private fun setCheckedStateForView(viewId: Int, checked: Boolean) {
val checkedView = findViewById<View>(viewId)
if (checkedView != null && checkedView is RadioButton) {
checkedView.isChecked = checked
}
}
fun setOnCheckedChangeListener(listener: OnCheckedChangeListener?) {
onCheckedChangeListener = listener
}
fun clearCheck() {
setCheckedStateForView(checkedRadioButtonId, false)
checkedRadioButtonId = -1
}
private fun adjustTableLayout() {
if (maxColumns > 0 && maxRows > 0) {
val childCount = childCount
var rowCount = 0
var currentRow: TableRow? = null
for (i in 0 until childCount) {
val child = getChildAt(i)
if (child is TableRow) {
currentRow = child
rowCount++
} else if (currentRow != null) {
val columnCount = currentRow.childCount
if (columnCount >= maxColumns) {
// Create a new row if the maximum column count is reached
if (rowCount < maxRows) {
val newRow = TableRow(context)
super.addView(newRow, getChildIndex(currentRow) + 1)
currentRow = newRow
rowCount++
} else {
// Remove extra children that exceed the maximum row count
removeView(child)
continue
}
}
}
}
}
}
private fun getChildIndex(child: View): Int {
val childCount = childCount
for (i in 0 until childCount) {
if (getChildAt(i) === child) {
return i
}
}
return -1
}
interface OnCheckedChangeListener {
fun onCheckedChanged(group: TableRadioGroup?, checkedId: Int)
}
}

View File

@ -19,7 +19,8 @@ class ArcaGetter : BaseGetter {
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
}
override fun doWork(): Result {
override fun realWork(): Result {
try {
val urls = arrayListOf(
"https://arca.live/b/singbung?mode=best",

View File

@ -1,13 +1,26 @@
package rasel.lunar.launcher.workers
import android.content.Context
import androidx.annotation.CallSuper
import androidx.work.Worker
import androidx.work.WorkerParameters
import rasel.lunar.launcher.model.RssData
import rasel.lunar.launcher.utils.before30Min
import rasel.lunar.launcher.utils.beforeDay
import java.util.Calendar
import java.util.Date
open class BaseGetter : Worker {
open abstract class BaseGetter : Worker {
protected companion object {
var lastedUpdateTime = 0L
fun before10Min(): Long {
val cal: Calendar = Calendar.getInstance()
cal.setTime(Date())
cal.add(Calendar.MINUTE, -10)
return cal.timeInMillis
}
}
val USAGT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15"
val now = Date()
val limitDateTime = beforeDay(now,3)
@ -17,7 +30,15 @@ open class BaseGetter : Worker {
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
}
@CallSuper
override fun doWork(): Result {
TODO("Not yet implemented")
val currentTime = before10Min()
if (lastedUpdateTime > 0L && currentTime > lastedUpdateTime) {
return Result.success().apply {
}
}
return realWork()
}
abstract fun realWork() : Result
}

View File

@ -67,7 +67,7 @@ class ClienGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
try {
val testUrl2 = arrayListOf("https://www.clien.net/service/group/community")

View File

@ -66,7 +66,7 @@ class DCGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
var tempArray = arrayListOf<RssData>()

View File

@ -18,7 +18,7 @@ class DotaxGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
try {
val dotaxUrls = arrayListOf("https://m.cafe.daum.net/dotax",
"https://m.cafe.daum.net/dotax/_rec?page=2",

View File

@ -17,7 +17,7 @@ class FmKoreaGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
val now = Date()
try {
val fmkoreaUrls = arrayListOf("https://www.fmkorea.com",

View File

@ -17,7 +17,7 @@ class NewsFeedsGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
feddsUrls.clear()
feddsUrls.addAll(RssList.newsFeeds)
feddsUrls.addAll(RssList.getFeedUrls())

View File

@ -42,7 +42,7 @@ class RecentCallGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
var dateParam = beforeDay(Date(),3).toString()
var managedCursor = lActivity?.contentResolver?.query(
@ -74,33 +74,13 @@ class RecentCallGetter : BaseGetter {
var dir: String = ""
val dircode = callType.toInt()
when (dircode) {
CallLog.Calls.INCOMING_TYPE -> {
dir = "INCOMING_TYPE"
}
CallLog.Calls.OUTGOING_TYPE -> {
dir = "OUTGOING_TYPE"
}
CallLog.Calls.MISSED_TYPE -> {
dir = "MISSED_TYPE"
}
CallLog.Calls.VOICEMAIL_TYPE -> {
dir = "VOICEMAIL_TYPE"
}
CallLog.Calls.REJECTED_TYPE -> {
dir = "REJECTED_TYPE"
}
CallLog.Calls.BLOCKED_TYPE -> {
dir = "BLOCKED_TYPE"
}
CallLog.Calls.ANSWERED_EXTERNALLY_TYPE -> {
dir = "ANSWERED_EXTERNALLY_TYPE"
}
CallLog.Calls.INCOMING_TYPE -> { dir = "INCOMING_TYPE" }
CallLog.Calls.OUTGOING_TYPE -> { dir = "OUTGOING_TYPE" }
CallLog.Calls.MISSED_TYPE -> { dir = "MISSED_TYPE" }
CallLog.Calls.VOICEMAIL_TYPE -> { dir = "VOICEMAIL_TYPE" }
CallLog.Calls.REJECTED_TYPE -> { dir = "REJECTED_TYPE" }
CallLog.Calls.BLOCKED_TYPE -> { dir = "BLOCKED_TYPE" }
CallLog.Calls.ANSWERED_EXTERNALLY_TYPE -> { dir = "ANSWERED_EXTERNALLY_TYPE" }
}
var missed: RecentCall = if (recentCalls.containsKey(phNumber)) {
recentCalls.get(phNumber)!!.apply {

View File

@ -29,7 +29,7 @@ class RecentSmsGetter : BaseGetter {
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
var dateParam = beforeDay(Date(),3).toString()
val managedCursor = lActivity?.contentResolver?.query(
Telephony.Sms.CONTENT_URI, arrayOf(

View File

@ -16,7 +16,7 @@ class RedditGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
val temp = arrayListOf<RssData>()
for (url in feedJsons) {
for (it in RssFeedsParser.getReddit(url)) {

View File

@ -2,6 +2,7 @@ package rasel.lunar.launcher.workers
import android.annotation.SuppressLint
import android.content.Context
import androidx.core.net.toUri
import androidx.work.WorkerParameters
import org.jsoup.Jsoup
import rasel.lunar.launcher.model.DcInside
@ -47,7 +48,7 @@ class RuliWebGetter : BaseGetter {
if (title.length > 0 && pageLink.length > 0) {
RuliWeb().let { ru ->
ru.title = title
ru.link = pageLink
ru.link = pageLink.replace(pageLink.toUri().query ?: "","")
ru.desc = desc
ru.thumbnail = thumbnailUrl
ru.dateTiem = dateTimeTxt.replace("날짜","").trim()
@ -59,7 +60,7 @@ class RuliWebGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
try {
val testUrl2 = arrayListOf("https://bbs.ruliweb.com/best/humor_only","https://bbs.ruliweb.com/best/humor_only/now?m=humor_only&t=default&page=2")

View File

@ -45,7 +45,7 @@ class TheQooGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
try {
val testUrl2 = arrayListOf("https://theqoo.net/hot")

View File

@ -44,7 +44,7 @@ object WorkersDb {
try {
getRealm().writeBlocking {
try {
this.copyToRealm(it, UpdatePolicy.ALL)
this.copyToRealm(it, UpdatePolicy.ERROR)
} catch (e : Exception) {
}

View File

@ -23,7 +23,7 @@ class YoutubeGetter : BaseGetter {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
override fun realWork(): Result {
rssUrls.clear()
rssUrls.addAll(RssList.youtubeUrls)
for (url in rssUrls) {
@ -41,8 +41,6 @@ class YoutubeGetter : BaseGetter {
if(it.html().contains("var ytInitialData", false)) {/**/
var ytInitialData = it.html().split("var ytInitialData = ")[1].split("</script>")[0].toString()
var tempJSONObject : JSONObject? = null
JSONObject(ytInitialData).apply{
tempJSONObject = this
val root = Gson().fromJson(tempJSONObject.toString(), Root::class.java)

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<rasel.lunar.launcher.view.TableRadioGroup
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:id="@+id/categoryz"
android:maxRows="5"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</rasel.lunar.launcher.view.TableRadioGroup>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/colorInputLayout"
android:layout_width="@dimen/oneNinetySix"
android:layout_height="wrap_content"
android:hint="inpout text"
app:boxBackgroundColor="?attr/colorSurface"
app:endIconMode="clear_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/categoryz"
>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,3 +1,4 @@
<paths>
<external-path name="apk_files" path="." />
<external-path name="external_files" path="." />
</paths>