├── .gitignore
├── .idea
├── .gitignore
└── .name
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── harshrajpurohit
│ │ └── letsbrowse
│ │ ├── activity
│ │ ├── BookmarkActivity.kt
│ │ └── MainActivity.kt
│ │ ├── adapter
│ │ ├── BookmarkAdapter.kt
│ │ └── TabAdapter.kt
│ │ ├── fragment
│ │ ├── BrowseFragment.kt
│ │ └── HomeFragment.kt
│ │ └── model
│ │ ├── Bookmark.kt
│ │ └── Tab.kt
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ ├── bg_gradient.jpg
│ ├── custom_design.xml
│ ├── custom_tabs_textview.xml
│ ├── ic_add.xml
│ ├── ic_back.xml
│ ├── ic_bookmark.xml
│ ├── ic_cancel.xml
│ ├── ic_d_google.png
│ ├── ic_d_youtube.png
│ ├── ic_desktop.xml
│ ├── ic_forward.xml
│ ├── ic_fullscreen.xml
│ ├── ic_fullscreen_exit.xml
│ ├── ic_globe.xml
│ ├── ic_home.xml
│ ├── ic_refresh.xml
│ ├── ic_save.xml
│ ├── ic_search.xml
│ └── ic_settings.xml
│ ├── layout
│ ├── activity_bookmark.xml
│ ├── activity_main.xml
│ ├── bookmark_dialog.xml
│ ├── bookmark_view.xml
│ ├── fragment_browse.xml
│ ├── fragment_home.xml
│ ├── long_bookmark_view.xml
│ ├── more_features.xml
│ ├── tab.xml
│ └── tabs_view.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── values-night
│ └── themes.xml
│ ├── values
│ ├── colors.xml
│ ├── ic_launcher_background.xml
│ ├── strings.xml
│ └── themes.xml
│ └── xml
│ ├── activity_main_scene.xml
│ ├── backup_rules.xml
│ └── data_extraction_rules.xml
├── app_screenshots
├── 1.png
├── 2.png
├── 4.png
├── 6.png
├── Lets Browse v1.0.1 10-07-2024.apk
├── bottom_dialog.png
└── tabs.png
├── build.gradle.kts
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | Lets Browse
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Let's Browse
2 |
3 |  
4 |
5 | **Let's Browse** is a privacy-first **Kotlin-based Android browser** designed to deliver fast, secure, and feature-rich web browsing. Built natively in **Kotlin**, it is lightweight, efficient, and tailored for **Android** users who value simplicity with modern functionality.
6 |
7 | ---
8 |
9 | ## 📥 Download APK
10 |
11 | Try the app yourself, and I’d love to hear your feedback! 😊
12 |
13 | - [Download APK v1.0.1](https://drive.google.com/file/d/1e6dVExS0qPWzTjoJliW4MhRnT2WHsHGh/view?usp=drivesdk)
14 |
15 | ---
16 |
17 | ## 📸 Screenshots
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | ---
31 |
32 | ## 🌟 Features
33 |
34 | - **Bookmark Webpages**: Save your favorite sites for quick access.
35 | - **Save as PDF**: Capture and save webpages as PDFs for offline use.
36 | - **Desktop Site Mode**: Easily switch to desktop view.
37 | - **Multi-Tabs Support**: Open and manage multiple tabs at once.
38 | - **Fullscreen Browsing**: Enjoy an immersive browsing experience.
39 | - **Material Design**: Clean, modern UI using the latest Material Design elements.
40 | - **Private and Secure**: Built with privacy in mind to protect user data.
41 | - **Fast and Lightweight**: Minimal resource usage for smooth performance.
42 | - **Open-Source**: Built entirely in Kotlin for Android, easy to extend or modify.
43 |
44 | ---
45 |
46 | ## 🎥 YouTube Playlist
47 |
48 | Watch the full development series on YouTube:
49 | [Check out the Playlist](https://www.youtube.com/playlist?list=PL8kbUJtS6hyaR6N8_i1YgOpZTDn2TP_uH)
50 |
51 | ---
52 |
53 | ## 💬 Feedback and Suggestions
54 |
55 | For any feedback or suggestions, feel free to reach out via email:
56 | 📧 [rajpurohitharsh2020@gmail.com](mailto:rajpurohitharsh2020@gmail.com)
57 |
58 | ---
59 |
60 | ## 🏷️ Keywords
61 |
62 | - **Kotlin Web Browser**
63 | - **Android Native Browser**
64 | - **Kotlin ExoPlayer Integration**
65 | - **Android Privacy Browser**
66 | - **Native Kotlin Browser for Android**
67 | - **Kotlin Android Development**
68 | - **Kotlin Webview**
69 | - **Browser with PDF Save**
70 | - **Android Multi-Tab Browser**
71 | - **Lightweight Browser in Kotlin**
72 |
73 | ---
74 |
75 | ## 💡 Note
76 |
77 | This project is more improved and optimized than the YouTube course project. It includes new features and better performance.
78 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.harshRajpurohit.letsBrowse"
8 | compileSdk = 34
9 |
10 | defaultConfig {
11 | applicationId = "com.harshRajpurohit.letsBrowse"
12 | minSdk = 21
13 | targetSdk = 34
14 | versionCode = 1
15 | versionName = "1.0.1"
16 | }
17 |
18 | buildTypes {
19 | release {
20 | isMinifyEnabled = false
21 | proguardFiles(
22 | getDefaultProguardFile("proguard-android-optimize.txt"),
23 | "proguard-rules.pro"
24 | )
25 | }
26 | }
27 | compileOptions {
28 | sourceCompatibility = JavaVersion.VERSION_1_8
29 | targetCompatibility = JavaVersion.VERSION_1_8
30 | }
31 | kotlinOptions {
32 | jvmTarget = "1.8"
33 | }
34 |
35 | buildFeatures{
36 | // For viewBinding
37 | viewBinding = true
38 | }
39 | }
40 |
41 | dependencies {
42 |
43 | implementation(libs.androidx.core.ktx)
44 | implementation(libs.androidx.appcompat)
45 | implementation(libs.material)
46 | implementation(libs.androidx.activity)
47 | implementation(libs.androidx.constraintlayout)
48 |
49 | // Pull to Refresh
50 | implementation(libs.legacy.support)
51 |
52 | // For storing objects in shared preferences
53 | implementation(libs.gson)
54 |
55 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
17 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harshrajpurohit/letsbrowse/activity/BookmarkActivity.kt:
--------------------------------------------------------------------------------
1 | package com.harshRajpurohit.letsBrowse.activity
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.recyclerview.widget.LinearLayoutManager
6 | import com.harshRajpurohit.letsBrowse.adapter.BookmarkAdapter
7 | import com.harshRajpurohit.letsBrowse.databinding.ActivityBookmarkBinding
8 |
9 | class BookmarkActivity : AppCompatActivity() {
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 | val binding = ActivityBookmarkBinding.inflate(layoutInflater)
13 | setContentView(binding.root)
14 | binding.rvBookmarks.setItemViewCacheSize(5)
15 | binding.rvBookmarks.hasFixedSize()
16 | binding.rvBookmarks.layoutManager = LinearLayoutManager(this)
17 | binding.rvBookmarks.adapter = BookmarkAdapter(this, isActivity = true)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harshrajpurohit/letsbrowse/activity/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.harshRajpurohit.letsBrowse.activity
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.graphics.Bitmap
6 | import android.graphics.Color
7 | import android.graphics.drawable.ColorDrawable
8 | import android.net.ConnectivityManager
9 | import android.net.NetworkCapabilities
10 | import android.os.Build
11 | import android.os.Bundle
12 | import android.print.PrintAttributes
13 | import android.print.PrintJob
14 | import android.print.PrintManager
15 | import android.util.Log
16 | import android.view.Gravity
17 | import android.view.WindowManager
18 | import android.webkit.WebView
19 | import androidx.appcompat.app.AlertDialog
20 | import androidx.appcompat.app.AppCompatActivity
21 | import androidx.core.content.ContextCompat
22 | import androidx.core.content.res.ResourcesCompat
23 | import androidx.core.view.WindowCompat
24 | import androidx.core.view.WindowInsetsCompat
25 | import androidx.core.view.WindowInsetsControllerCompat
26 | import androidx.fragment.app.Fragment
27 | import androidx.fragment.app.FragmentManager
28 | import androidx.lifecycle.Lifecycle
29 | import androidx.recyclerview.widget.LinearLayoutManager
30 | import androidx.viewpager2.adapter.FragmentStateAdapter
31 | import androidx.viewpager2.widget.ViewPager2
32 | import com.google.android.material.button.MaterialButton
33 | import com.google.android.material.dialog.MaterialAlertDialogBuilder
34 | import com.google.android.material.snackbar.Snackbar
35 | import com.google.android.material.textview.MaterialTextView
36 | import com.google.gson.GsonBuilder
37 | import com.google.gson.reflect.TypeToken
38 | import com.harshRajpurohit.letsBrowse.R
39 | import com.harshRajpurohit.letsBrowse.activity.MainActivity.Companion.myPager
40 | import com.harshRajpurohit.letsBrowse.activity.MainActivity.Companion.tabsBtn
41 | import com.harshRajpurohit.letsBrowse.adapter.TabAdapter
42 | import com.harshRajpurohit.letsBrowse.databinding.ActivityMainBinding
43 | import com.harshRajpurohit.letsBrowse.databinding.BookmarkDialogBinding
44 | import com.harshRajpurohit.letsBrowse.databinding.MoreFeaturesBinding
45 | import com.harshRajpurohit.letsBrowse.databinding.TabsViewBinding
46 | import com.harshRajpurohit.letsBrowse.fragment.BrowseFragment
47 | import com.harshRajpurohit.letsBrowse.fragment.HomeFragment
48 | import com.harshRajpurohit.letsBrowse.model.Bookmark
49 | import com.harshRajpurohit.letsBrowse.model.Tab
50 | import java.io.ByteArrayOutputStream
51 | import java.net.URL
52 | import java.text.SimpleDateFormat
53 | import java.util.*
54 |
55 | class MainActivity : AppCompatActivity() {
56 |
57 | lateinit var binding: ActivityMainBinding
58 | private var printJob: PrintJob? = null
59 |
60 | companion object {
61 | var tabsList: ArrayList = ArrayList()
62 | private var isFullscreen: Boolean = true
63 | var isDesktopSite: Boolean = false
64 | var bookmarkList: ArrayList = ArrayList()
65 | var bookmarkIndex: Int = -1
66 | lateinit var myPager: ViewPager2
67 | lateinit var tabsBtn: MaterialTextView
68 | }
69 |
70 | override fun onRestart() {
71 | super.onRestart()
72 | Log.e("Restart", "onRestart")
73 | }
74 |
75 | override fun onCreate(savedInstanceState: Bundle?) {
76 | super.onCreate(savedInstanceState)
77 |
78 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
79 | window.attributes.layoutInDisplayCutoutMode =
80 | WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
81 | }
82 | binding = ActivityMainBinding.inflate(layoutInflater)
83 | setContentView(binding.root)
84 |
85 | getAllBookmarks()
86 |
87 | tabsList.add(Tab("Home", HomeFragment()))
88 | binding.myPager.adapter = TabsAdapter(supportFragmentManager, lifecycle)
89 | binding.myPager.isUserInputEnabled = false
90 | myPager = binding.myPager
91 | tabsBtn = binding.tabsBtn
92 |
93 | initializeView()
94 | changeFullscreen(enable = true)
95 | }
96 |
97 | @SuppressLint("NotifyDataSetChanged")
98 | override fun onBackPressed() {
99 | var frag: BrowseFragment? = null
100 | try {
101 | frag = tabsList[binding.myPager.currentItem].fragment as BrowseFragment
102 | } catch (_: Exception) {
103 | }
104 |
105 | when {
106 | frag?.binding?.webView?.canGoBack() == true -> frag.binding.webView.goBack()
107 | binding.myPager.currentItem != 0 -> {
108 | tabsList.removeAt(binding.myPager.currentItem)
109 | binding.myPager.adapter?.notifyDataSetChanged()
110 | binding.myPager.currentItem = tabsList.size - 1
111 |
112 | }
113 |
114 | else -> super.onBackPressed()
115 | }
116 | }
117 |
118 |
119 | private inner class TabsAdapter(fa: FragmentManager, lc: Lifecycle) :
120 | FragmentStateAdapter(fa, lc) {
121 | override fun getItemCount(): Int = tabsList.size
122 |
123 | override fun createFragment(position: Int): Fragment = tabsList[position].fragment
124 | }
125 |
126 |
127 | private fun initializeView() {
128 |
129 | binding.tabsBtn.setOnClickListener {
130 | val viewTabs = layoutInflater.inflate(R.layout.tabs_view, binding.root, false)
131 | val bindingTabs = TabsViewBinding.bind(viewTabs)
132 |
133 | val dialogTabs =
134 | MaterialAlertDialogBuilder(this, R.style.roundCornerDialog).setView(viewTabs)
135 | .setTitle("Select Tab")
136 | .setPositiveButton("Home") { self, _ ->
137 | changeTab("Home", HomeFragment())
138 | self.dismiss()
139 | }
140 | .setNeutralButton("Google") { self, _ ->
141 | changeTab("Google", BrowseFragment(urlNew = "www.google.com"))
142 | self.dismiss()
143 | }
144 | .create()
145 |
146 | bindingTabs.tabsRV.setHasFixedSize(true)
147 | bindingTabs.tabsRV.layoutManager = LinearLayoutManager(this)
148 | bindingTabs.tabsRV.adapter = TabAdapter(this, dialogTabs)
149 |
150 | dialogTabs.show()
151 |
152 | val pBtn = dialogTabs.getButton(AlertDialog.BUTTON_POSITIVE)
153 | val nBtn = dialogTabs.getButton(AlertDialog.BUTTON_NEUTRAL)
154 |
155 | pBtn.isAllCaps = false
156 | nBtn.isAllCaps = false
157 |
158 | pBtn.setTextColor(Color.BLACK)
159 | nBtn.setTextColor(Color.BLACK)
160 |
161 | pBtn.setCompoundDrawablesWithIntrinsicBounds(
162 | ResourcesCompat.getDrawable(resources, R.drawable.ic_home, theme), null, null, null
163 | )
164 | nBtn.setCompoundDrawablesWithIntrinsicBounds(
165 | ResourcesCompat.getDrawable(resources, R.drawable.ic_add, theme), null, null, null
166 | )
167 | }
168 |
169 | binding.settingBtn.setOnClickListener {
170 |
171 | var frag: BrowseFragment? = null
172 | try {
173 | frag = tabsList[binding.myPager.currentItem].fragment as BrowseFragment
174 | } catch (_: Exception) {
175 | }
176 |
177 | val view = layoutInflater.inflate(R.layout.more_features, binding.root, false)
178 | val dialogBinding = MoreFeaturesBinding.bind(view)
179 |
180 | val dialog = MaterialAlertDialogBuilder(this).setView(view).create()
181 |
182 | dialog.window?.apply {
183 | attributes.gravity = Gravity.BOTTOM
184 | attributes.y = 50
185 | setBackgroundDrawable(ColorDrawable(0xFFFFFFFF.toInt()))
186 | }
187 | dialog.show()
188 |
189 | if (isFullscreen) {
190 | dialogBinding.fullscreenBtn.apply {
191 | setIconTintResource(R.color.cool_blue)
192 | setTextColor(ContextCompat.getColor(this@MainActivity, R.color.cool_blue))
193 | }
194 | }
195 |
196 | frag?.let {
197 | bookmarkIndex = isBookmarked(it.binding.webView.url!!)
198 | if (bookmarkIndex != -1) {
199 |
200 | dialogBinding.bookmarkBtn.apply {
201 | setIconTintResource(R.color.cool_blue)
202 | setTextColor(ContextCompat.getColor(this@MainActivity, R.color.cool_blue))
203 | }
204 | }
205 | }
206 |
207 | if (isDesktopSite) {
208 | dialogBinding.desktopBtn.apply {
209 | setIconTintResource(R.color.cool_blue)
210 | setTextColor(ContextCompat.getColor(this@MainActivity, R.color.cool_blue))
211 | }
212 | }
213 |
214 |
215 |
216 | dialogBinding.backBtn.setOnClickListener {
217 | onBackPressed()
218 | }
219 |
220 | dialogBinding.forwardBtn.setOnClickListener {
221 | frag?.apply {
222 | if (binding.webView.canGoForward())
223 | binding.webView.goForward()
224 | }
225 | }
226 |
227 | dialogBinding.saveBtn.setOnClickListener {
228 | dialog.dismiss()
229 | if (frag != null)
230 | saveAsPdf(web = frag.binding.webView)
231 | else Snackbar.make(binding.root, "First Open A WebPage\uD83D\uDE03", 3000).show()
232 | }
233 |
234 | dialogBinding.fullscreenBtn.setOnClickListener {
235 | it as MaterialButton
236 |
237 | isFullscreen = if (isFullscreen) {
238 | changeFullscreen(enable = false)
239 | it.setIconTintResource(R.color.black)
240 | it.setTextColor(ContextCompat.getColor(this, R.color.black))
241 | false
242 | } else {
243 | changeFullscreen(enable = true)
244 | it.setIconTintResource(R.color.cool_blue)
245 | it.setTextColor(ContextCompat.getColor(this, R.color.cool_blue))
246 | true
247 | }
248 | }
249 |
250 | dialogBinding.desktopBtn.setOnClickListener {
251 | it as MaterialButton
252 |
253 | frag?.binding?.webView?.apply {
254 | isDesktopSite = if (isDesktopSite) {
255 | settings.userAgentString = null
256 | it.setIconTintResource(R.color.black)
257 | it.setTextColor(ContextCompat.getColor(this@MainActivity, R.color.black))
258 | false
259 | } else {
260 | settings.userAgentString =
261 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0"
262 | settings.useWideViewPort = true
263 | evaluateJavascript(
264 | "document.querySelector('meta[name=\"viewport\"]').setAttribute('content'," +
265 | " 'width=1024px, initial-scale=' + (document.documentElement.clientWidth / 1024));",
266 | null
267 | )
268 | it.setIconTintResource(R.color.cool_blue)
269 | it.setTextColor(
270 | ContextCompat.getColor(
271 | this@MainActivity,
272 | R.color.cool_blue
273 | )
274 | )
275 | true
276 | }
277 | reload()
278 | dialog.dismiss()
279 | }
280 |
281 | }
282 |
283 | dialogBinding.bookmarkBtn.setOnClickListener {
284 | frag?.let {
285 | if (bookmarkIndex == -1) {
286 | val viewB =
287 | layoutInflater.inflate(R.layout.bookmark_dialog, binding.root, false)
288 | val bBinding = BookmarkDialogBinding.bind(viewB)
289 | val dialogB = MaterialAlertDialogBuilder(this)
290 | .setTitle("Add Bookmark")
291 | .setMessage("Url:${it.binding.webView.url}")
292 | .setPositiveButton("Add") { self, _ ->
293 | try {
294 | val array = ByteArrayOutputStream()
295 | it.webIcon?.compress(Bitmap.CompressFormat.PNG, 100, array)
296 | bookmarkList.add(
297 | Bookmark(
298 | name = bBinding.bookmarkTitle.text.toString(),
299 | url = it.binding.webView.url!!,
300 | array.toByteArray()
301 | )
302 | )
303 | } catch (e: Exception) {
304 | bookmarkList.add(
305 | Bookmark(
306 | name = bBinding.bookmarkTitle.text.toString(),
307 | url = it.binding.webView.url!!
308 | )
309 | )
310 | }
311 | self.dismiss()
312 | }
313 | .setNegativeButton("Cancel") { self, _ -> self.dismiss() }
314 | .setView(viewB).create()
315 | dialogB.show()
316 | bBinding.bookmarkTitle.setText(it.binding.webView.title)
317 | } else {
318 | val dialogB = MaterialAlertDialogBuilder(this)
319 | .setTitle("Remove Bookmark")
320 | .setMessage("Url:${it.binding.webView.url}")
321 | .setPositiveButton("Remove") { self, _ ->
322 | bookmarkList.removeAt(bookmarkIndex)
323 | self.dismiss()
324 | }
325 | .setNegativeButton("Cancel") { self, _ -> self.dismiss() }
326 | .create()
327 | dialogB.show()
328 | }
329 | }
330 |
331 | dialog.dismiss()
332 | }
333 | }
334 |
335 | }
336 |
337 | override fun onResume() {
338 | super.onResume()
339 | printJob?.let {
340 | when {
341 | it.isCompleted -> Snackbar.make(
342 | binding.root,
343 | "Successful -> ${it.info.label}",
344 | 4000
345 | ).show()
346 |
347 | it.isFailed -> Snackbar.make(binding.root, "Failed -> ${it.info.label}", 4000)
348 | .show()
349 | }
350 | }
351 | }
352 |
353 | private fun saveAsPdf(web: WebView) {
354 | val pm = getSystemService(Context.PRINT_SERVICE) as PrintManager
355 |
356 | val jobName = "${URL(web.url).host}_${
357 | SimpleDateFormat("HH:mm d_MMM_yy", Locale.ENGLISH)
358 | .format(Calendar.getInstance().time)
359 | }"
360 | val printAdapter = web.createPrintDocumentAdapter(jobName)
361 | val printAttributes = PrintAttributes.Builder()
362 | printJob = pm.print(jobName, printAdapter, printAttributes.build())
363 | }
364 |
365 | private fun changeFullscreen(enable: Boolean) {
366 | if (enable) {
367 | WindowCompat.setDecorFitsSystemWindows(window, false)
368 | WindowInsetsControllerCompat(window, binding.root).let { controller ->
369 | controller.hide(WindowInsetsCompat.Type.systemBars())
370 | controller.systemBarsBehavior =
371 | WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
372 | }
373 | } else {
374 | WindowCompat.setDecorFitsSystemWindows(window, true)
375 | WindowInsetsControllerCompat(
376 | window,
377 | binding.root
378 | ).show(WindowInsetsCompat.Type.systemBars())
379 | }
380 | }
381 |
382 | fun isBookmarked(url: String): Int {
383 | bookmarkList.forEachIndexed { index, bookmark ->
384 | if (bookmark.url == url) return index
385 | }
386 | return -1
387 | }
388 |
389 | fun saveBookmarks() {
390 | //for storing bookmarks data using shared preferences
391 | val editor = getSharedPreferences("BOOKMARKS", MODE_PRIVATE).edit()
392 |
393 | val data = GsonBuilder().create().toJson(bookmarkList)
394 | editor.putString("bookmarkList", data)
395 |
396 | editor.apply()
397 | }
398 |
399 | private fun getAllBookmarks() {
400 | //for getting bookmarks data using shared preferences from storage
401 | bookmarkList = ArrayList()
402 | val editor = getSharedPreferences("BOOKMARKS", MODE_PRIVATE)
403 | val data = editor.getString("bookmarkList", null)
404 |
405 | if (data != null) {
406 | val list: ArrayList = GsonBuilder().create()
407 | .fromJson(data, object : TypeToken>() {}.type)
408 | bookmarkList.addAll(list)
409 | } else {
410 | // add default bookmarks
411 | bookmarkList.add(
412 | Bookmark(
413 | "Google",
414 | "https://www.google.com",
415 | null,
416 | R.drawable.ic_d_google
417 | )
418 | )
419 | bookmarkList.add(
420 | Bookmark(
421 | "Youtube",
422 | "https://youtube.com",
423 | null,
424 | R.drawable.ic_d_youtube
425 | )
426 | )
427 | }
428 | }
429 |
430 | }
431 |
432 |
433 | @SuppressLint("NotifyDataSetChanged")
434 | fun changeTab(url: String, fragment: Fragment, isBackground: Boolean = false) {
435 | MainActivity.tabsList.add(Tab(name = url, fragment = fragment))
436 | myPager.adapter?.notifyDataSetChanged()
437 | tabsBtn.text = MainActivity.tabsList.size.toString()
438 |
439 | if (!isBackground) myPager.currentItem = MainActivity.tabsList.size - 1
440 | }
441 |
442 | @Suppress("DEPRECATION")
443 | fun checkForInternet(context: Context): Boolean {
444 | val connectivityManager =
445 | context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
446 |
447 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
448 | val network = connectivityManager.activeNetwork ?: return false
449 | val activeNetwork = connectivityManager.getNetworkCapabilities(network) ?: return false
450 | activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
451 | activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
452 | } else {
453 | val networkInfo = connectivityManager.activeNetworkInfo ?: return false
454 | networkInfo.isConnected
455 | }
456 | }
457 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harshrajpurohit/letsbrowse/adapter/BookmarkAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.harshRajpurohit.letsBrowse.adapter
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.graphics.BitmapFactory
6 | import android.view.LayoutInflater
7 | import android.view.ViewGroup
8 | import androidx.core.content.res.ResourcesCompat
9 | import androidx.core.graphics.drawable.toDrawable
10 | import androidx.recyclerview.widget.RecyclerView
11 | import com.google.android.material.snackbar.Snackbar
12 | import com.harshRajpurohit.letsBrowse.R
13 | import com.harshRajpurohit.letsBrowse.activity.MainActivity
14 | import com.harshRajpurohit.letsBrowse.activity.changeTab
15 | import com.harshRajpurohit.letsBrowse.activity.checkForInternet
16 | import com.harshRajpurohit.letsBrowse.databinding.BookmarkViewBinding
17 | import com.harshRajpurohit.letsBrowse.databinding.LongBookmarkViewBinding
18 | import com.harshRajpurohit.letsBrowse.fragment.BrowseFragment
19 |
20 | class BookmarkAdapter(private val context: Context, private val isActivity: Boolean = false) :
21 | RecyclerView.Adapter() {
22 |
23 | private val colors = context.resources.getIntArray(R.array.myColors)
24 |
25 | class MyHolder(
26 | binding: BookmarkViewBinding? = null,
27 | bindingL: LongBookmarkViewBinding? = null
28 | ) : RecyclerView.ViewHolder((binding?.root ?: bindingL?.root)!!) {
29 | val image = (binding?.bookmarkIcon ?: bindingL?.bookmarkIcon)!!
30 | val name = (binding?.bookmarkName ?: bindingL?.bookmarkName)!!
31 | val root = (binding?.root ?: bindingL?.root)!!
32 | }
33 |
34 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
35 | if (isActivity)
36 | return MyHolder(
37 | bindingL = LongBookmarkViewBinding.inflate(
38 | LayoutInflater.from(context),
39 | parent,
40 | false
41 | )
42 | )
43 | return MyHolder(
44 | binding = BookmarkViewBinding.inflate(
45 | LayoutInflater.from(context),
46 | parent,
47 | false
48 | )
49 | )
50 | }
51 |
52 | override fun onBindViewHolder(holder: MyHolder, position: Int) {
53 | try {
54 | //default bookmark image
55 | if (MainActivity.bookmarkList[position].imagePath != null) {
56 | holder.image.background = ResourcesCompat.getDrawable(context.resources, MainActivity.bookmarkList[position].imagePath!!, context.theme)
57 | } else {
58 | val icon = BitmapFactory.decodeByteArray(
59 | MainActivity.bookmarkList[position].image, 0,
60 | MainActivity.bookmarkList[position].image!!.size
61 | )
62 | holder.image.background = icon.toDrawable(context.resources)
63 | }
64 | } catch (e: Exception) {
65 | holder.image.setBackgroundColor(colors[(colors.indices).random()])
66 | holder.image.text = MainActivity.bookmarkList[position].name[0].toString()
67 | }
68 |
69 | holder.name.text = MainActivity.bookmarkList[position].name
70 |
71 |
72 | holder.root.setOnClickListener {
73 | when {
74 | checkForInternet(context) -> {
75 | changeTab(
76 | MainActivity.bookmarkList[position].name,
77 | BrowseFragment(urlNew = MainActivity.bookmarkList[position].url)
78 | )
79 | if (isActivity) (context as Activity).finish()
80 | }
81 |
82 | else -> Snackbar.make(holder.root, "Internet Not Connected\uD83D\uDE03", 3000)
83 | .show()
84 | }
85 |
86 | }
87 | }
88 |
89 | override fun getItemCount(): Int {
90 | return MainActivity.bookmarkList.size
91 | }
92 | }
93 |
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harshrajpurohit/letsbrowse/adapter/TabAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.harshRajpurohit.letsBrowse.adapter
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.view.LayoutInflater
6 | import android.view.ViewGroup
7 | import androidx.appcompat.app.AlertDialog
8 | import androidx.recyclerview.widget.RecyclerView
9 | import com.google.android.material.snackbar.Snackbar
10 | import com.harshRajpurohit.letsBrowse.activity.MainActivity
11 | import com.harshRajpurohit.letsBrowse.databinding.TabBinding
12 |
13 | class TabAdapter(private val context: Context, private val dialog: AlertDialog): RecyclerView.Adapter() {
14 |
15 | class MyHolder(binding: TabBinding) :RecyclerView.ViewHolder(binding.root) {
16 | val cancelBtn = binding.cancelBtn
17 | val name = binding.tabName
18 | val root = binding.root
19 | }
20 |
21 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
22 | return MyHolder(TabBinding.inflate(LayoutInflater.from(context), parent, false))
23 | }
24 |
25 | @SuppressLint("NotifyDataSetChanged")
26 | override fun onBindViewHolder(holder: MyHolder, position: Int) {
27 | holder.name.text = MainActivity.tabsList[position].name
28 | holder.root.setOnClickListener {
29 | MainActivity.myPager.currentItem = position
30 | dialog.dismiss()
31 | }
32 |
33 | holder.cancelBtn.setOnClickListener {
34 | if(MainActivity.tabsList.size == 1 || position == MainActivity.myPager.currentItem)
35 | Snackbar.make(MainActivity.myPager, "Can't Remove this tab", 3000).show()
36 | else{
37 | MainActivity.tabsList.removeAt(position)
38 | notifyDataSetChanged()
39 | MainActivity.myPager.adapter?.notifyItemRemoved(position)
40 | }
41 |
42 |
43 | }
44 | }
45 |
46 | override fun getItemCount(): Int {
47 | return MainActivity.tabsList.size
48 | }
49 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harshrajpurohit/letsbrowse/fragment/BrowseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.harshRajpurohit.letsBrowse.fragment
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Intent
5 | import android.content.res.Resources
6 | import android.graphics.Bitmap
7 | import android.graphics.BitmapFactory
8 | import android.graphics.Color
9 | import android.graphics.drawable.ColorDrawable
10 | import android.net.Uri
11 | import android.os.Bundle
12 | import android.os.Handler
13 | import android.provider.MediaStore
14 | import android.text.SpannableStringBuilder
15 | import android.util.Base64
16 | import android.view.*
17 | import android.webkit.*
18 | import androidx.core.app.ShareCompat
19 | import androidx.fragment.app.Fragment
20 | import com.google.android.material.dialog.MaterialAlertDialogBuilder
21 | import com.google.android.material.imageview.ShapeableImageView
22 | import com.google.android.material.snackbar.Snackbar
23 | import com.harshRajpurohit.letsBrowse.R
24 | import com.harshRajpurohit.letsBrowse.activity.MainActivity
25 | import com.harshRajpurohit.letsBrowse.activity.changeTab
26 | import com.harshRajpurohit.letsBrowse.databinding.FragmentBrowseBinding
27 | import java.io.ByteArrayOutputStream
28 |
29 | class BrowseFragment(private var urlNew: String = "https://www.google.com") : Fragment() {
30 |
31 | lateinit var binding: FragmentBrowseBinding
32 | var webIcon: Bitmap? = null
33 |
34 |
35 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
36 | val view = inflater.inflate(R.layout.fragment_browse, container, false)
37 | binding = FragmentBrowseBinding.bind(view)
38 | registerForContextMenu(binding.webView)
39 |
40 | binding.webView.apply {
41 | when{
42 | URLUtil.isValidUrl(urlNew) -> loadUrl(urlNew)
43 | urlNew.contains(".com", ignoreCase = true) -> loadUrl(urlNew)
44 | else -> loadUrl("https://www.google.com/search?q=$urlNew")
45 | }
46 | }
47 |
48 | return view
49 | }
50 |
51 | @SuppressLint("SetJavaScriptEnabled", "ClickableViewAccessibility")
52 | override fun onResume() {
53 | super.onResume()
54 | MainActivity.tabsList[MainActivity.myPager.currentItem].name = binding.webView.url.toString()
55 | MainActivity.tabsBtn.text = MainActivity.tabsList.size.toString()
56 |
57 | //for downloading file using external download manager
58 | binding.webView.setDownloadListener { url, _, _, _, _ -> startActivity(Intent(Intent.ACTION_VIEW).setData(
59 | Uri.parse(url))) }
60 |
61 | val mainRef = requireActivity() as MainActivity
62 |
63 | mainRef.binding.refreshBtn.visibility = View.VISIBLE
64 | mainRef.binding.refreshBtn.setOnClickListener {
65 | binding.webView.reload()
66 | }
67 |
68 | binding.webView.apply {
69 | settings.javaScriptEnabled = true
70 | settings.setSupportZoom(true)
71 | settings.builtInZoomControls = true
72 | settings.displayZoomControls = false
73 | webViewClient = object: WebViewClient(){
74 |
75 | override fun onLoadResource(view: WebView?, url: String?) {
76 | super.onLoadResource(view, url)
77 | if(MainActivity.isDesktopSite)
78 | view?.evaluateJavascript("document.querySelector('meta[name=\"viewport\"]').setAttribute('content'," +
79 | " 'width=1024px, initial-scale=' + (document.documentElement.clientWidth / 1024));", null)
80 | }
81 |
82 | override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) {
83 | super.doUpdateVisitedHistory(view, url, isReload)
84 | mainRef.binding.topSearchBar.text = SpannableStringBuilder(url)
85 | MainActivity.tabsList[MainActivity.myPager.currentItem].name = url.toString()
86 | }
87 |
88 | override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
89 | super.onPageStarted(view, url, favicon)
90 | mainRef.binding.progressBar.progress = 0
91 | mainRef.binding.progressBar.visibility = View.VISIBLE
92 | if(url!!.contains("you", ignoreCase = false)) mainRef.binding.root.transitionToEnd()
93 | }
94 |
95 | override fun onPageFinished(view: WebView?, url: String?) {
96 | super.onPageFinished(view, url)
97 | mainRef.binding.progressBar.visibility = View.GONE
98 | binding.webView.zoomOut()
99 | }
100 | }
101 | webChromeClient = object: WebChromeClient(){
102 | //for setting icon to our search bar
103 | override fun onReceivedIcon(view: WebView?, icon: Bitmap?) {
104 | super.onReceivedIcon(view, icon)
105 | try{
106 | mainRef.binding.webIcon.setImageBitmap(icon)
107 | webIcon = icon
108 | MainActivity.bookmarkIndex = mainRef.isBookmarked(view?.url!!)
109 | if(MainActivity.bookmarkIndex != -1){
110 | val array = ByteArrayOutputStream()
111 | icon!!.compress(Bitmap.CompressFormat.PNG, 100, array)
112 | MainActivity.bookmarkList[MainActivity.bookmarkIndex].image = array.toByteArray()
113 | }
114 | }catch (e: Exception){}
115 | }
116 |
117 | override fun onShowCustomView(view: View?, callback: CustomViewCallback?) {
118 | super.onShowCustomView(view, callback)
119 | binding.webView.visibility = View.GONE
120 | binding.customView.visibility = View.VISIBLE
121 | binding.customView.addView(view)
122 | mainRef.binding.root.transitionToEnd()
123 | }
124 |
125 | override fun onHideCustomView() {
126 | super.onHideCustomView()
127 | binding.webView.visibility = View.VISIBLE
128 | binding.customView.visibility = View.GONE
129 |
130 | }
131 |
132 | override fun onProgressChanged(view: WebView?, newProgress: Int) {
133 | super.onProgressChanged(view, newProgress)
134 | mainRef.binding.progressBar.progress = newProgress
135 | }
136 | }
137 |
138 | binding.webView.setOnTouchListener { _, motionEvent ->
139 | mainRef.binding.root.onTouchEvent(motionEvent)
140 | return@setOnTouchListener false
141 | }
142 |
143 | binding.webView.reload()
144 | }
145 |
146 |
147 | }
148 |
149 | override fun onPause() {
150 | super.onPause()
151 | (requireActivity() as MainActivity).saveBookmarks()
152 | //for clearing all webview data
153 | binding.webView.apply {
154 | clearMatches()
155 | clearHistory()
156 | clearFormData()
157 | clearSslPreferences()
158 | clearCache(true)
159 |
160 | CookieManager.getInstance().removeAllCookies(null)
161 | WebStorage.getInstance().deleteAllData()
162 | }
163 |
164 | }
165 |
166 | override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
167 | super.onCreateContextMenu(menu, v, menuInfo)
168 |
169 | val result = binding.webView.hitTestResult
170 | when(result.type){
171 | WebView.HitTestResult.IMAGE_TYPE -> {
172 | menu.add("View Image")
173 | menu.add("Save Image")
174 | menu.add("Share")
175 | menu.add("Close")
176 | }
177 | WebView.HitTestResult.SRC_ANCHOR_TYPE, WebView.HitTestResult.ANCHOR_TYPE-> {
178 | menu.add("Open in New Tab")
179 | menu.add("Open Tab in Background")
180 | menu.add("Share")
181 | menu.add("Close")
182 | }
183 | WebView.HitTestResult.EDIT_TEXT_TYPE, WebView.HitTestResult.UNKNOWN_TYPE -> {}
184 | else ->{
185 | menu.add("Open in New Tab")
186 | menu.add("Open Tab in Background")
187 | menu.add("Share")
188 | menu.add("Close")
189 | }
190 | }
191 | }
192 |
193 | override fun onContextItemSelected(item: MenuItem): Boolean {
194 |
195 | val message = Handler().obtainMessage()
196 | binding.webView.requestFocusNodeHref(message)
197 | val url = message.data.getString("url")
198 | val imgUrl = message.data.getString("src")
199 |
200 | when(item.title){
201 | "Open in New Tab" -> {
202 | changeTab(url.toString(), BrowseFragment(url.toString()))
203 | }
204 | "Open Tab in Background" ->{
205 | changeTab(url.toString(), BrowseFragment(url.toString()), isBackground = true)
206 | }
207 | "View Image" ->{
208 | if(imgUrl != null) {
209 | if (imgUrl.contains("base64")) {
210 | val pureBytes = imgUrl.substring(imgUrl.indexOf(",") + 1)
211 | val decodedBytes = Base64.decode(pureBytes, Base64.DEFAULT)
212 | val finalImg =
213 | BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size)
214 |
215 | val imgView = ShapeableImageView(requireContext())
216 | imgView.setImageBitmap(finalImg)
217 |
218 | val imgDialog = MaterialAlertDialogBuilder(requireContext()).setView(imgView).create()
219 | imgDialog.show()
220 |
221 | imgView.layoutParams.width = Resources.getSystem().displayMetrics.widthPixels
222 | imgView.layoutParams.height = (Resources.getSystem().displayMetrics.heightPixels * .75).toInt()
223 | imgView.requestLayout()
224 |
225 | imgDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
226 |
227 | }
228 | else changeTab(imgUrl, BrowseFragment(imgUrl))
229 | }
230 | }
231 |
232 | "Save Image" ->{
233 | if(imgUrl != null) {
234 | if (imgUrl.contains("base64")) {
235 | val pureBytes = imgUrl.substring(imgUrl.indexOf(",") + 1)
236 | val decodedBytes = Base64.decode(pureBytes, Base64.DEFAULT)
237 | val finalImg =
238 | BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size)
239 |
240 | MediaStore.Images.Media.insertImage(
241 | requireActivity().contentResolver,
242 | finalImg, "Image", null
243 | )
244 | Snackbar.make(binding.root, "Image Saved Successfully", 3000).show()
245 | }
246 | else startActivity(Intent(Intent.ACTION_VIEW).setData(Uri.parse(imgUrl)))
247 | }
248 | }
249 |
250 | "Share" -> {
251 | val tempUrl = url ?: imgUrl
252 | if(tempUrl != null){
253 | if(tempUrl.contains("base64")){
254 |
255 | val pureBytes = tempUrl.substring(tempUrl.indexOf(",") + 1)
256 | val decodedBytes = Base64.decode(pureBytes, Base64.DEFAULT)
257 | val finalImg = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size)
258 |
259 | val path = MediaStore.Images.Media.insertImage(requireActivity().contentResolver,
260 | finalImg, "Image", null)
261 |
262 | ShareCompat.IntentBuilder(requireContext()).setChooserTitle("Sharing Url!")
263 | .setType("image/*")
264 | .setStream(Uri.parse(path))
265 | .startChooser()
266 | }
267 | else{
268 | ShareCompat.IntentBuilder(requireContext()).setChooserTitle("Sharing Url!")
269 | .setType("text/plain").setText(tempUrl)
270 | .startChooser()
271 | }
272 | }
273 | else Snackbar.make(binding.root, "Not a Valid Link!", 3000).show()
274 | }
275 | "Close" -> {}
276 | }
277 |
278 | return super.onContextItemSelected(item)
279 | }
280 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harshrajpurohit/letsbrowse/fragment/HomeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.harshRajpurohit.letsBrowse.fragment
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.widget.SearchView
9 | import androidx.fragment.app.Fragment
10 | import androidx.recyclerview.widget.GridLayoutManager
11 | import com.google.android.material.snackbar.Snackbar
12 | import com.harshRajpurohit.letsBrowse.R
13 | import com.harshRajpurohit.letsBrowse.activity.BookmarkActivity
14 | import com.harshRajpurohit.letsBrowse.activity.MainActivity
15 | import com.harshRajpurohit.letsBrowse.activity.changeTab
16 | import com.harshRajpurohit.letsBrowse.activity.checkForInternet
17 | import com.harshRajpurohit.letsBrowse.adapter.BookmarkAdapter
18 | import com.harshRajpurohit.letsBrowse.databinding.FragmentHomeBinding
19 |
20 |
21 | class HomeFragment : Fragment() {
22 |
23 | private lateinit var binding: FragmentHomeBinding
24 |
25 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
26 | val view = inflater.inflate(R.layout.fragment_home, container, false)
27 | binding = FragmentHomeBinding.bind(view)
28 |
29 | return view
30 | }
31 |
32 | override fun onResume() {
33 | super.onResume()
34 |
35 | val mainActivityRef = requireActivity() as MainActivity
36 |
37 | MainActivity.tabsBtn.text = MainActivity.tabsList.size.toString()
38 | MainActivity.tabsList[MainActivity.myPager.currentItem].name = "Home"
39 |
40 | mainActivityRef.binding.topSearchBar.setText("")
41 | binding.searchView.setQuery("",false)
42 | mainActivityRef.binding.webIcon.setImageResource(R.drawable.ic_search)
43 |
44 | mainActivityRef.binding.refreshBtn.visibility = View.GONE
45 |
46 | binding.searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener{
47 | override fun onQueryTextSubmit(result: String?): Boolean {
48 | if(checkForInternet(requireContext()))
49 | changeTab(result!!, BrowseFragment(result))
50 | else
51 | Snackbar.make(binding.root, "Internet Not Connected\uD83D\uDE03", 3000).show()
52 | return true
53 | }
54 | override fun onQueryTextChange(p0: String?): Boolean = false
55 | })
56 | mainActivityRef.binding.goBtn.setOnClickListener {
57 | if(checkForInternet(requireContext()))
58 | changeTab(mainActivityRef.binding.topSearchBar.text.toString(),
59 | BrowseFragment(mainActivityRef.binding.topSearchBar.text.toString())
60 | )
61 | else
62 | Snackbar.make(binding.root, "Internet Not Connected\uD83D\uDE03", 3000).show()
63 | }
64 |
65 | binding.recyclerView.setHasFixedSize(true)
66 | binding.recyclerView.setItemViewCacheSize(5)
67 | binding.recyclerView.layoutManager = GridLayoutManager(requireContext(), 5)
68 | binding.recyclerView.adapter = BookmarkAdapter(requireContext())
69 |
70 | if(MainActivity.bookmarkList.size < 1)
71 | binding.viewAllBtn.visibility = View.GONE
72 | binding.viewAllBtn.setOnClickListener {
73 | startActivity(Intent(requireContext(), BookmarkActivity::class.java))
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harshrajpurohit/letsbrowse/model/Bookmark.kt:
--------------------------------------------------------------------------------
1 | package com.harshRajpurohit.letsBrowse.model
2 |
3 | data class Bookmark(val name: String, val url: String, var image: ByteArray? = null, var imagePath: Int? = null)
4 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harshrajpurohit/letsbrowse/model/Tab.kt:
--------------------------------------------------------------------------------
1 | package com.harshRajpurohit.letsBrowse.model
2 |
3 | import androidx.fragment.app.Fragment
4 |
5 | data class Tab(var name: String, val fragment: Fragment)
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_gradient.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/drawable/bg_gradient.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/custom_design.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/custom_tabs_textview.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_back.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_bookmark.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_cancel.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_d_google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/drawable/ic_d_google.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_d_youtube.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/drawable/ic_d_youtube.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_desktop.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_forward.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fullscreen.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fullscreen_exit.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_globe.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_home.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_refresh.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_save.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_search.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_settings.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_bookmark.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
23 |
24 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
26 |
27 |
32 |
33 |
43 |
44 |
55 |
56 |
64 |
65 |
74 |
75 |
76 |
77 |
78 |
84 |
85 |
86 |
87 |
95 |
96 |
97 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/bookmark_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/bookmark_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_browse.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
23 |
24 |
34 |
35 |
36 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/long_bookmark_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
20 |
21 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/more_features.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
25 |
26 |
38 |
39 |
51 |
52 |
53 |
54 |
55 |
59 |
60 |
72 |
73 |
85 |
86 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
27 |
28 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/tabs_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
11 |
12 |
13 | #0091EA
14 | #64DD17
15 | #F50057
16 | #FFD600
17 |
18 |
19 | - @color/cool_green
20 | - @color/cool_pink
21 | - @color/black
22 | - @color/cool_blue
23 | - @color/purple_500
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Lets Browse
3 | Enter Url
4 | Go Button
5 | Settings Button
6 | Search
7 |
8 | HomePage
9 | Bookmark
10 | Back
11 | Forward
12 | FullScreen
13 | Save
14 | Desktop
15 | Bookmark Name
16 | View All
17 | All Bookmarks
18 | Refresh
19 | 1
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
18 |
23 |
24 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/activity_main_scene.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
27 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app_screenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app_screenshots/1.png
--------------------------------------------------------------------------------
/app_screenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app_screenshots/2.png
--------------------------------------------------------------------------------
/app_screenshots/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app_screenshots/4.png
--------------------------------------------------------------------------------
/app_screenshots/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app_screenshots/6.png
--------------------------------------------------------------------------------
/app_screenshots/Lets Browse v1.0.1 10-07-2024.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app_screenshots/Lets Browse v1.0.1 10-07-2024.apk
--------------------------------------------------------------------------------
/app_screenshots/bottom_dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app_screenshots/bottom_dialog.png
--------------------------------------------------------------------------------
/app_screenshots/tabs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/app_screenshots/tabs.png
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.android.application) apply false
4 | alias(libs.plugins.jetbrains.kotlin.android) apply false
5 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. For more details, visit
12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.5.0"
3 | kotlin = "1.9.0"
4 | coreKtx = "1.13.1"
5 | appcompat = "1.7.0"
6 | material = "1.12.0"
7 | activity = "1.9.0"
8 | constraintlayout = "2.1.4"
9 |
10 | legacy-support = "1.0.0"
11 | gson = "2.10.1"
12 |
13 | [libraries]
14 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
15 | androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
16 | material = { group = "com.google.android.material", name = "material", version.ref = "material" }
17 | androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
18 | androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
19 |
20 | gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
21 | legacy-support = { group = "androidx.legacy", name = "legacy-support-v4", version.ref = "legacy-support" }
22 |
23 | [plugins]
24 | android-application = { id = "com.android.application", version.ref = "agp" }
25 | jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
26 |
27 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HarshAndroid/LetsBrowse-Android-Webview-Kotlin/17628e1aa5afb67b893e2075d7fd7fb384594be9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jul 04 17:49:51 IST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google {
4 | content {
5 | includeGroupByRegex("com\\.android.*")
6 | includeGroupByRegex("com\\.google.*")
7 | includeGroupByRegex("androidx.*")
8 | }
9 | }
10 | mavenCentral()
11 | gradlePluginPortal()
12 | }
13 | }
14 | dependencyResolutionManagement {
15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
16 | repositories {
17 | google()
18 | mavenCentral()
19 | }
20 | }
21 |
22 | rootProject.name = "LetsBrowse"
23 | include(":app")
24 |
--------------------------------------------------------------------------------