diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 981c606..c0ae2b6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,6 +34,10 @@ tools:ignore="QueryAllPackagesPermission" /> + + + @@ -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 @@ + + \ No newline at end of file diff --git a/app/src/main/java/android/print/PDFPrint.java b/app/src/main/java/android/print/PDFPrint.java new file mode 100644 index 0000000..68c84fe --- /dev/null +++ b/app/src/main/java/android/print/PDFPrint.java @@ -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); + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt b/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt index 0623e78..98e0a54 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt @@ -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(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(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(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(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(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(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(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(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(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(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(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?) { diff --git a/app/src/main/kotlin/rasel/lunar/launcher/feeds/Feeds.kt b/app/src/main/kotlin/rasel/lunar/launcher/feeds/Feeds.kt index 0c69a23..1c20df6 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/feeds/Feeds.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/feeds/Feeds.kt @@ -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().apply { + }) "onews" -> home?.queryInfos(arrayListOf().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() diff --git a/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt b/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt index 090079f..04c51c8 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/home/LauncherHome.kt @@ -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? = null - var lastedNoti : RealmResults? = null + var lasted : List? = null + var lastedNoti : List? = 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 -> @@ -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().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().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 -> + 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(R.id.input) as EditText + val categoryz = viewInflated.findViewById(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) } diff --git a/app/src/main/kotlin/rasel/lunar/launcher/model/CommunityData.kt b/app/src/main/kotlin/rasel/lunar/launcher/model/CommunityData.kt index 143809f..eaeb93f 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/model/CommunityData.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/model/CommunityData.kt @@ -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!! } } diff --git a/app/src/main/kotlin/rasel/lunar/launcher/todos/RssItemAdapter.kt b/app/src/main/kotlin/rasel/lunar/launcher/todos/RssItemAdapter.kt index 08283b5..dee6064 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/todos/RssItemAdapter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/todos/RssItemAdapter.kt @@ -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() { - private var rssDataItemLis: ArrayList = 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(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 = 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("&","&").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(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) { 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, var newList: List diff --git a/app/src/main/kotlin/rasel/lunar/launcher/view/TableRadioGroup.kt b/app/src/main/kotlin/rasel/lunar/launcher/view/TableRadioGroup.kt new file mode 100644 index 0000000..96937ec --- /dev/null +++ b/app/src/main/kotlin/rasel/lunar/launcher/view/TableRadioGroup.kt @@ -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(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) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/ArcaGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/ArcaGetter.kt index 25a2282..30b2d00 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/ArcaGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/ArcaGetter.kt @@ -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", diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/BaseGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/BaseGetter.kt index a2086ae..8f1ddaa 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/BaseGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/BaseGetter.kt @@ -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 } \ No newline at end of file diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/ClienGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/ClienGetter.kt index df2bf18..3e62879 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/ClienGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/ClienGetter.kt @@ -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") diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/DCGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/DCGetter.kt index 5016bb1..0583c6a 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/DCGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/DCGetter.kt @@ -66,7 +66,7 @@ class DCGetter : BaseGetter { } @SuppressLint("RestrictedApi") - override fun doWork(): Result { + override fun realWork(): Result { var tempArray = arrayListOf() diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/DotaxGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/DotaxGetter.kt index b1df42f..c85b81c 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/DotaxGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/DotaxGetter.kt @@ -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", diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/FmKoreaGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/FmKoreaGetter.kt index 3993237..7bba89c 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/FmKoreaGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/FmKoreaGetter.kt @@ -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", diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/NewsFeedsGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/NewsFeedsGetter.kt index 011bb31..494ddec 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/NewsFeedsGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/NewsFeedsGetter.kt @@ -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()) diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/RecentCallGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/RecentCallGetter.kt index 7238fb6..263fa1f 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/RecentCallGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/RecentCallGetter.kt @@ -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 { diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/RecentSmsGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/RecentSmsGetter.kt index d3921bd..f960500 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/RecentSmsGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/RecentSmsGetter.kt @@ -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( diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/RedditGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/RedditGetter.kt index 38df0cf..4997725 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/RedditGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/RedditGetter.kt @@ -16,7 +16,7 @@ class RedditGetter : BaseGetter { } @SuppressLint("RestrictedApi") - override fun doWork(): Result { + override fun realWork(): Result { val temp = arrayListOf() for (url in feedJsons) { for (it in RssFeedsParser.getReddit(url)) { diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/RuliWebGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/RuliWebGetter.kt index 4ad5f3d..d05fd88 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/RuliWebGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/RuliWebGetter.kt @@ -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") diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/TheQooGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/TheQooGetter.kt index 93328ab..46e08dd 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/TheQooGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/TheQooGetter.kt @@ -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") diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/WorkersDb.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/WorkersDb.kt index 21f78ea..a9855f8 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/WorkersDb.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/WorkersDb.kt @@ -44,7 +44,7 @@ object WorkersDb { try { getRealm().writeBlocking { try { - this.copyToRealm(it, UpdatePolicy.ALL) + this.copyToRealm(it, UpdatePolicy.ERROR) } catch (e : Exception) { } diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/YoutubeGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/YoutubeGetter.kt index a8e8152..5e66608 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/YoutubeGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/YoutubeGetter.kt @@ -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("")[0].toString() var tempJSONObject : JSONObject? = null - - JSONObject(ytInitialData).apply{ tempJSONObject = this val root = Gson().fromJson(tempJSONObject.toString(), Root::class.java) diff --git a/app/src/main/res/layout/search_layout.xml b/app/src/main/res/layout/search_layout.xml new file mode 100644 index 0000000..651bfdb --- /dev/null +++ b/app/src/main/res/layout/search_layout.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml index a8df289..6ce9181 100644 --- a/app/src/main/res/xml/file_paths.xml +++ b/app/src/main/res/xml/file_paths.xml @@ -1,3 +1,4 @@ + \ No newline at end of file