android/taildrop: support direct mode for incoming taildrop (#251)

Updates tailscale/corp#18202

Implements direct mode support for incoming taildrop files.  None of the localAPI endpoints are implemented here but this will get taildrop files to the right places.

Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
pull/255/head
Jonathan Nobels 2 months ago committed by GitHub
parent 5599f2ddeb
commit 61fb6bbf8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -109,7 +109,14 @@ class App : Application(), libtailscale.AppContext {
override fun onCreate() {
super.onCreate()
val dataDir = this.filesDir.absolutePath
app = Libtailscale.start(dataDir, this)
// Set this to enable direct mode for taildrop whereby downloads will be saved directly
// to the given folder. We will preferentially use <shared>/Downloads and fallback to
// an app local directory "Taildrop" if we cannot create that. This mode does not support
// user notifications for incoming files.
val directFileDir = this.prepareDownloadsFolder()
app = Libtailscale.start(dataDir, directFileDir.absolutePath, this)
Request.setApp(app)
Notifier.setApp(app)
connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
@ -464,4 +471,27 @@ class App : Application(), libtailscale.AppContext {
}
}
}
fun prepareDownloadsFolder(): File {
var downloads = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
try {
if (!downloads.exists()) {
downloads.mkdirs()
}
} catch (e: Exception) {
Log.e(TAG, "Failed to create downloads folder: $e")
downloads = File(this.filesDir, "Taildrop")
try {
if (!downloads.exists()) {
downloads.mkdirs()
}
} catch (e: Exception) {
Log.e(TAG, "Failed to create Taildrop folder: $e")
downloads = File("")
}
}
return downloads
}
}

@ -35,6 +35,10 @@ import (
type App struct {
dataDir string
// enables direct file mode for the taildrop manager
directFileRoot string
// appCtx is a global reference to the com.tailscale.ipn.App instance.
appCtx AppContext
@ -46,7 +50,7 @@ type App struct {
ready sync.WaitGroup
}
func start(dataDir string, appCtx AppContext) Application {
func start(dataDir, directFileRoot string, appCtx AppContext) Application {
defer func() {
if p := recover(); p != nil {
log.Printf("panic in Start %s: %s", p, debug.Stack())
@ -70,7 +74,7 @@ func start(dataDir string, appCtx AppContext) Application {
os.Setenv("HOME", dataDir)
}
return newApp(dataDir, appCtx)
return newApp(dataDir, directFileRoot, appCtx)
}
type backend struct {
@ -113,7 +117,7 @@ func (a *App) runBackend(ctx context.Context) error {
}
configs := make(chan configPair)
configErrs := make(chan error)
b, err := newBackend(a.dataDir, a.appCtx, a.store, func(rcfg *router.Config, dcfg *dns.OSConfig) error {
b, err := newBackend(a.dataDir, a.directFileRoot, a.appCtx, a.store, func(rcfg *router.Config, dcfg *dns.OSConfig) error {
if rcfg == nil {
return nil
}
@ -228,7 +232,7 @@ func (a *App) runBackend(ctx context.Context) error {
}
}
func newBackend(dataDir string, appCtx AppContext, store *stateStore,
func newBackend(dataDir, directFileRoot string, appCtx AppContext, store *stateStore,
settings settingsFunc) (*backend, error) {
sys := new(tsd.System)
@ -299,6 +303,8 @@ func newBackend(dataDir string, appCtx AppContext, store *stateStore,
engine.Close()
return nil, fmt.Errorf("runBackend: NewLocalBackend: %v", err)
}
lb.SetDirectFileRoot(directFileRoot)
if err := ns.Start(lb); err != nil {
return nil, fmt.Errorf("startNetstack: %w", err)
}

@ -26,50 +26,10 @@ var (
// onGoogleToken receives google ID tokens.
onGoogleToken = make(chan string)
// onWriteStorageGranted is notified when we are granted WRITE_STORAGE_PERMISSION.
onWriteStorageGranted = make(chan struct{}, 1)
// onDNSConfigChanged is notified when the network changes and the DNS config needs to be updated.
onDNSConfigChanged = make(chan struct{}, 1)
)
func OnShareIntent(nfiles int32, types []int32, mimes []string, items []string, names []string, sizes []int) {
// TODO(oxtoacart): actually implement this
// const (
// typeNone = 0
// typeInline = 1
// typeURI = 2
// )
// jenv := (*jni.Env)(unsafe.Pointer(env))
// var files []File
// for i := 0; i < int(nfiles); i++ {
// f := File{
// Type: FileType(types[i]),
// MIMEType: mimes[i],
// Name: names[i],
// }
// if f.Name == "" {
// f.Name = "file.bin"
// }
// switch f.Type {
// case FileTypeText:
// f.Text = items[i]
// f.Size = int64(len(f.Text))
// case FileTypeURI:
// f.URI = items[i]
// f.Size = sizes[i]
// default:
// panic("unknown file type")
// }
// files = append(files, f)
// }
// select {
// case <-onFileShare:
// default:
// }
// onFileShare <- files
}
func OnDnsConfigChanged() {
select {
case onDNSConfigChanged <- struct{}{}:
@ -77,14 +37,6 @@ func OnDnsConfigChanged() {
}
}
//export Java_com_tailscale_ipn_App_onWriteStorageGranted
func OnWriteStorageGranted() {
select {
case onWriteStorageGranted <- struct{}{}:
default:
}
}
func notifyVPNPrepared() {
select {
case onVPNPrepared <- struct{}{}:

@ -7,8 +7,8 @@ import _ "golang.org/x/mobile/bind"
// Start starts the application, storing state in the given dataDir and using
// the given appCtx.
func Start(dataDir string, appCtx AppContext) Application {
return start(dataDir, appCtx)
func Start(dataDir, directFileRoot string, appCtx AppContext) Application {
return start(dataDir, directFileRoot, appCtx)
}
// AppContext provides a context within which the Application is running. This

@ -30,10 +30,11 @@ const (
customLoginServerPrefKey = "customloginserver"
)
func newApp(dataDir string, appCtx AppContext) Application {
func newApp(dataDir, directFileRoot string, appCtx AppContext) Application {
a := &App{
dataDir: dataDir,
appCtx: appCtx,
directFileRoot: directFileRoot,
dataDir: dataDir,
appCtx: appCtx,
}
a.ready.Add(1)

Loading…
Cancel
Save