Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="33" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.amaze.cloud.permission.ACCESS_PROVIDER" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ package com.amaze.filemanager.ui.dialogs
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.graphics.toColorInt
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
Expand Down Expand Up @@ -142,11 +142,8 @@ class SmbSearchDialog : DialogFragment() {
context: Context,
) : RecyclerView.Adapter<ViewHolder>() {
private val items: MutableList<ComputerParcelable> = ArrayList()
private val mInflater: LayoutInflater

init {
mInflater = context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE) as LayoutInflater
}
private val inflater: LayoutInflater =
context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE) as LayoutInflater

/**
* Called by [ComputerParcelableViewModel], add found computer to list view
Expand Down Expand Up @@ -194,12 +191,12 @@ class SmbSearchDialog : DialogFragment() {
val view: View
return when (viewType) {
VIEW_PROGRESSBAR -> {
view = mInflater.inflate(R.layout.smb_progress_row, parent, false)
view = inflater.inflate(R.layout.smb_progress_row, parent, false)
ViewHolder(view)
}
else -> {
view =
mInflater.inflate(R.layout.smb_computers_row, parent, false)
inflater.inflate(R.layout.smb_computers_row, parent, false)
ElementViewHolder(view)
}
}
Expand Down Expand Up @@ -229,7 +226,7 @@ class SmbSearchDialog : DialogFragment() {
holder.txtTitle.text = name
holder.image.setImageResource(R.drawable.ic_settings_remote_white_48dp)
if (utilsProvider.appTheme == AppTheme.LIGHT) {
holder.image.setColorFilter(Color.parseColor("#666666"))
holder.image.setColorFilter("#666666".toColorInt())
}
holder.txtDesc.text = addr
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright (C) 2014-2024 Arpit Khurana <arpitkh96@gmail.com>, Vishal Nehra <vishalmeham2@gmail.com>,
* Emmanuel Messulam<emmanuelbendavid@gmail.com>, Raymond Lai <airwave209gt at gmail.com> and Contributors.
*
* This file is part of Amaze File Manager.
*
* Amaze File Manager is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.amaze.filemanager.utils.smb

import android.content.Context.NSD_SERVICE
import android.content.Context.WIFI_SERVICE
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.net.wifi.WifiManager
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import com.amaze.filemanager.application.AppConfig
import com.amaze.filemanager.utils.ComputerParcelable
import org.slf4j.Logger
import org.slf4j.LoggerFactory

/**
* [SmbDeviceScannerObservable.DiscoverDeviceStrategy] implementation using Android's
* [NsdManager] to discover SMB devices using mDNS/Bonjour/ZeroConf.
*
* @see SmbDeviceScannerObservable
* @see NsdManager
*
*/
class NsdManagerDiscoverDeviceStrategy : SmbDeviceScannerObservable.DiscoverDeviceStrategy {
companion object {
internal const val SERVICE_TYPE_SMB = "_smb._tcp."
private val logger: Logger =
LoggerFactory.getLogger(NsdManagerDiscoverDeviceStrategy::class.java)
}

private val wifiManager: WifiManager =
AppConfig.getInstance().applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
private val nsdManager: NsdManager =
AppConfig.getInstance().applicationContext.getSystemService(NSD_SERVICE) as NsdManager

private var multicastLock: WifiManager.MulticastLock? = null
private var discoveryListener: NsdManager.DiscoveryListener? = null

override fun discoverDevices(callback: (ComputerParcelable) -> Unit) {
multicastLock =
wifiManager.createMulticastLock("smb_mdns_discovery").apply {
setReferenceCounted(true)
}
multicastLock?.acquire()
discoveryListener = createDiscoveryListener(callback)
nsdManager.discoverServices(
SERVICE_TYPE_SMB,
NsdManager.PROTOCOL_DNS_SD,
discoveryListener,
)
}

override fun onCancel() {
discoveryListener?.let {
nsdManager.stopServiceDiscovery(it)
discoveryListener = null
}
multicastLock?.let {
if (it.isHeld) {
it.release()
}
}
}

/**
* Creates a new [NsdManager.DiscoveryListener] to handle service discovery events.
*
* For backward compatibility, uses [NsdManager.ResolveListener] to resolve services
* and perform the callback.
*/
private fun createDiscoveryListener(callback: (ComputerParcelable) -> Unit): NsdManager.DiscoveryListener {
return object : NsdManager.DiscoveryListener {
override fun onServiceFound(serviceInfo: NsdServiceInfo) {
// Just to be sure.
if (serviceInfo.serviceType != SERVICE_TYPE_SMB) {
logger.warn("Unknown Service Type: ${serviceInfo.serviceType} for service: ${serviceInfo.serviceName}")
} else {
@Suppress("DEPRECATION")
nsdManager.resolveService(
serviceInfo,
object : NsdManager.ResolveListener {
override fun onServiceResolved(resolvedServiceInfo: NsdServiceInfo) {
val host =
if (SDK_INT >= UPSIDE_DOWN_CAKE) {
resolvedServiceInfo.hostAddresses.firstOrNull()
} else {
resolvedServiceInfo.host
}
if (host != null && host.hostAddress?.isNotEmpty() == true) {
val computer =
ComputerParcelable(
name = resolvedServiceInfo.serviceName,
addr = host.hostAddress!!,
)
callback(computer)
}
}

override fun onResolveFailed(
serviceInfo: NsdServiceInfo?,
errorCode: Int,
) {
logger.error(
"Service resolve failed: ${serviceInfo?.serviceName} with error code: $errorCode",
)
}
},
)
}
}

override fun onServiceLost(serviceInfo: NsdServiceInfo?) {
logger.debug("Service lost: ${serviceInfo?.serviceName}")
}

override fun onStartDiscoveryFailed(
serviceType: String,
errorCode: Int,
) {
logger.error("Service discovery start failed: $serviceType with error code: $errorCode")
nsdManager.stopServiceDiscovery(this)
}

override fun onStopDiscoveryFailed(
serviceType: String,
errorCode: Int,
) {
logger.debug("Service discovery stop failed: $serviceType with error code: $errorCode")
nsdManager.stopServiceDiscovery(this)
}

override fun onDiscoveryStarted(serviceType: String?) = logger.debug("Service discovery started: $serviceType")

override fun onDiscoveryStopped(serviceType: String?) = logger.debug("Service discovery stopped: $serviceType")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ package com.amaze.filemanager.utils.smb

import androidx.annotation.VisibleForTesting
import com.amaze.filemanager.utils.ComputerParcelable
import com.amaze.filemanager.utils.smb.SmbDeviceScannerObservable.DiscoverDeviceStrategy
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.disposables.Disposable
Expand Down Expand Up @@ -55,6 +54,7 @@ class SmbDeviceScannerObservable : Observable<ComputerParcelable>() {
arrayOf(
WsddDiscoverDeviceStrategy(),
SameSubnetDiscoverDeviceStrategy(),
NsdManagerDiscoverDeviceStrategy(),
)
@VisibleForTesting set

Expand Down Expand Up @@ -86,19 +86,22 @@ class SmbDeviceScannerObservable : Observable<ComputerParcelable>() {
*/
override fun subscribeActual(observer: Observer<in ComputerParcelable>) {
this.observer = observer
observer.onSubscribe(Disposables.empty())
this.disposable =
merge(
discoverDeviceStrategies.map { strategy ->
fromCallable {
create<ComputerParcelable> { emitter ->
strategy.discoverDevices { addr ->
observer.onNext(ComputerParcelable(addr.addr, addr.name))
if (!emitter.isDisposed) {
emitter.onNext(addr)
}
}
emitter.setCancellable { strategy.onCancel() }
}.subscribeOn(Schedulers.io())
},
).observeOn(Schedulers.computation()).doOnComplete {
discoverDeviceStrategies.forEach { strategy ->
strategy.onCancel()
}
}.subscribe()
).observeOn(Schedulers.computation()).subscribe(
{ computer -> observer.onNext(computer) },
{ error -> observer.onError(error) },
)
}
}
Loading
Loading