diff --git a/.gitignore b/.gitignore index d544b2d..2fe3458 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ *.jar ./pad cmd/pad/pad +cmd/pad/classes # IDE .idea/ diff --git a/cmd/pad/Permissions.java b/cmd/pad/Permissions.java index fac9484..310bb89 100644 --- a/cmd/pad/Permissions.java +++ b/cmd/pad/Permissions.java @@ -2,6 +2,7 @@ package st.wow.git.logbook; import java.lang.Runnable; import java.lang.String; +import android.os.Build; import android.os.Environment; import android.os.Handler; import android.content.Context; @@ -39,25 +40,49 @@ public class Permissions extends Fragment { }); } - @Override public void onAttach(Context ctx) { - super.onAttach(ctx); - Log.d("gio", "onAttach()"); - if (ctx instanceof Activity) { - Log.d("gio", "It's an Activity!"); - } - if (ctx.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ctx.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - Log.d("gio", "Requesting permissions"); - requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST); - } - if (!Environment.isExternalStorageManager()){ - Intent intent = new Intent(); - intent.setAction(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); - Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null); - intent.setData(uri); - startActivity(intent); - } - Log.d("gio", "Loading gio library"); - System.loadLibrary("gio"); + @Override + public void onAttach(Context context) { + super.onAttach(context); + Log.d("gio", "onAttach()"); + + if (!(context instanceof Activity)) { + Log.w("gio", "Context is not an Activity"); + return; + } + + Activity activity = (Activity) context; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + if (context.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + requestPermissions( + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, + PERMISSIONS_REQUEST + ); + } + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R + && !Environment.isExternalStorageManager()) { + Log.d("gio", "Requesting all files access"); + Intent intent = new Intent( + Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, + Uri.parse("package:" + activity.getPackageName()) + ); + if (intent.resolveActivity(activity.getPackageManager()) != null) { + startActivity(intent); + } else { + Intent fallback = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); + if (fallback.resolveActivity(activity.getPackageManager()) != null) { + startActivity(fallback); + } else { + Log.e("gio", "No activity found for all files access settings"); + } + } + } + + Log.d("gio", "Loading gio library"); + System.loadLibrary("gio"); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { diff --git a/cmd/pad/impl_android.go b/cmd/pad/impl_android.go index be6195f..4b7ba97 100644 --- a/cmd/pad/impl_android.go +++ b/cmd/pad/impl_android.go @@ -24,7 +24,7 @@ import ( type JNIEnv = C.JNIEnv var ( - startpath="/storage/emulated/0/Documents" + startpath="/storage/emulated/0/Notes" jvm uintptr theJVM *C.JavaVM ) diff --git a/cmd/pad/main.go b/cmd/pad/main.go index 1bd5ac2..3eb5d9f 100644 --- a/cmd/pad/main.go +++ b/cmd/pad/main.go @@ -4,7 +4,6 @@ import ( "flag" "log" "os" - "path/filepath" "sync" "gioui.org/app" @@ -41,12 +40,8 @@ func run(w *app.Window) error { rootDir := flag.String("root", startpath, "root directory for the filesystem") flag.Parse() - abs, err := filepath.Abs(*rootDir) - if err != nil { - log.Fatalf("invalid root directory: %v", err) - } - fs := &real.RealFileSystem{Root: abs} - log.Printf("using filesystem at %s", abs) + fs := real.NewRealFileSystem(*rootDir) + log.Printf("using filesystem at %s", fs.WorkingDir) logic := editor.NewLogic(fs) renderer := ui.New(ui.Theme{FontSize: 14}, shaper, logic.State()) @@ -62,6 +57,7 @@ func run(w *app.Window) error { for { switch e := w.Event().(type) { case app.DestroyEvent: + logic.Shutdown() return e.Err case app.ConfigEvent: // ConfigEvent: raw pixel dimensions only. diff --git a/internal/editor/logic.go b/internal/editor/logic.go index ba75870..511cc08 100644 --- a/internal/editor/logic.go +++ b/internal/editor/logic.go @@ -413,3 +413,10 @@ func (l *Logic) FlushAll() { } } } + +// Shutdown gracefully shuts down the logic goroutine and worker pool. +func (l *Logic) Shutdown() { + l.FlushAll() + l.Done() + l.workerPool.Stop() +} diff --git a/internal/io/pool/real/filesystem.go b/internal/io/pool/real/filesystem.go index 5d96f7e..d279ac4 100644 --- a/internal/io/pool/real/filesystem.go +++ b/internal/io/pool/real/filesystem.go @@ -9,11 +9,24 @@ import ( // RealFileSystem implements the pool.FileSystem interface using the real OS filesystem. type RealFileSystem struct { - Root string + WorkingDir string +} + +// NewRealFileSystem creates a RealFileSystem rooted at the given working directory. +// If workingDir is empty, it defaults to "/". +func NewRealFileSystem(workingDir string) *RealFileSystem { + if workingDir == "" { + workingDir = "/" + } + abs, err := filepath.Abs(workingDir) + if err != nil { + abs = workingDir + } + return &RealFileSystem{WorkingDir: abs} } func (fs *RealFileSystem) ReadDir(path string) ([]types.DirEntry, error) { - entries, err := os.ReadDir(filepath.Join(fs.Root, path)) + entries, err := os.ReadDir(filepath.Join(fs.WorkingDir, path)) if err != nil { return nil, err } @@ -25,21 +38,21 @@ func (fs *RealFileSystem) ReadDir(path string) ([]types.DirEntry, error) { } func (fs *RealFileSystem) DirExists(path string) bool { - info, err := os.Stat(filepath.Join(fs.Root, path)) + info, err := os.Stat(filepath.Join(fs.WorkingDir, path)) return err == nil && info.IsDir() } func (fs *RealFileSystem) FileExists(path string) bool { - info, err := os.Stat(filepath.Join(fs.Root, path)) + info, err := os.Stat(filepath.Join(fs.WorkingDir, path)) return err == nil && !info.IsDir() } func (fs *RealFileSystem) ReadFile(path string) ([]byte, error) { - return os.ReadFile(filepath.Join(fs.Root, path)) + return os.ReadFile(filepath.Join(fs.WorkingDir, path)) } func (fs *RealFileSystem) ReadFileAt(path string, offset, size int) ([]byte, error) { - fullPath := filepath.Join(fs.Root, path) + fullPath := filepath.Join(fs.WorkingDir, path) f, err := os.Open(fullPath) if err != nil { return nil, err @@ -61,7 +74,7 @@ func (fs *RealFileSystem) WriteFile(path string, content []byte) error { func (fs *RealFileSystem) WriteFileAtomic(path string, content []byte) error { // Atomic write implementation - fullPath := filepath.Join(fs.Root, path) + fullPath := filepath.Join(fs.WorkingDir, path) // Temp file in the same directory as the target to ensure same filesystem rename tmpPath := filepath.Join(filepath.Dir(fullPath), "."+filepath.Base(path)+".tmp") @@ -77,11 +90,11 @@ func (fs *RealFileSystem) WriteFileAtomic(path string, content []byte) error { } func (fs *RealFileSystem) DeleteFile(path string) error { - return os.Remove(filepath.Join(fs.Root, path)) + return os.Remove(filepath.Join(fs.WorkingDir, path)) } func (fs *RealFileSystem) CreateDir(path string) error { - return os.MkdirAll(filepath.Join(fs.Root, path), 0755) + return os.MkdirAll(filepath.Join(fs.WorkingDir, path), 0755) } // realDirEntry wraps os.DirEntry