Scoped storage methods for Android 10+
-
xabierpayet
- Posts: 280
- Joined: Fri Feb 24, 2012 9:34 am
Re: Scoped storage methods for Android 10+
do you knows if is possible, to get the leght sensor data from the android device?
I don´ty know how do it, and calljavamethod is not a very intuitive command for me.
I don´ty know how do it, and calljavamethod is not a very intuitive command for me.
Re: Scoped storage methods for Android 10+
I just had a quick round trying to get something usable from the help of AI, first I gave chatGPT a go, but I found the feedback a bit messy. Deepseek did create something that I was able to get some result from.xabierpayet wrote: ↑Thu Nov 20, 2025 7:28 pm do you knows if is possible, to get the leght sensor data from the android device?
I don´ty know how do it, and calljavamethod is not a very intuitive command for me.
Here is a full HollywoodDelegate.java file (one I had from before, but now the lightsensor part added)
Code: Select all
package com.amy66dev.extended;
// Core Android functionality
import android.content.Intent; // Camera, GPS, Sharing, Permissions
import android.net.Uri; // Camera, Sharing, File operations
import android.os.Build; // Version checks throughout
import android.os.Environment; // File operations, SD card, Storage
import android.util.Log; // Logging throughout
import android.app.Activity; // Base class, context
// Permissions and hardware features
import android.Manifest; // Camera, Location permissions
import android.content.pm.PackageManager; // Permission checking
import android.location.Location; // GPS functionality
import android.location.LocationManager; // GPS functionality
import android.media.MediaScannerConnection; // Camera file scanning
import android.os.Vibrator; // Vibration (currently commented out)
import android.provider.MediaStore; // Camera, MediaStore operations
import android.widget.Toast; // Toast messages (optional)
import android.content.Context; // System services
import android.content.ContentValues; // MediaStore operations
import android.media.MediaScannerConnection; // Camera file scanning (duplicate)
import android.content.ClipboardManager; // Clipboard functions
import android.content.ClipData; // Clipboard functions
import android.view.WindowManager; // Keep Screen On
// AndroidX libraries
import androidx.annotation.Nullable; // Nullable annotations
import androidx.core.app.ActivityCompat; // Permission requests
import androidx.core.content.ContextCompat; // Permission checking
import androidx.core.content.FileProvider; // File sharing
// Java utilities
import java.text.SimpleDateFormat; // Camera timestamp
import java.util.Date; // Camera timestamp
import java.util.Locale; // String formatting
import java.util.List; // GPS location providers
import java.util.ArrayList; // Multiple permissions requests
// File I/O operations
import java.io.File; // All file operations
import java.io.FileInputStream; // File reading, copy operations
import java.io.FileOutputStream; // File writing, copy operations
import java.io.IOException; // Exception handling
import java.io.BufferedReader; // File reading
import java.io.InputStreamReader; // File reading
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
/**
* HollywoodDelegate - Direct external storage file utilities.
* Must be included as Custom Code in the Hollywood APK Compiler.
*/
public class HollywoodDelegate extends HollywoodActivity {
private static final String TAG = "HollywoodDelegate";
// --- Add SD card helper HERE (inside the class, not before imports)
private File getExternalSdCard() {
File[] externalDirs = getApplicationContext().getExternalFilesDirs(null);
if (externalDirs != null && externalDirs.length > 1) {
for (File dir : externalDirs) {
if (dir != null && Environment.isExternalStorageRemovable(dir)) {
return dir.getParentFile().getParentFile().getParentFile().getParentFile();
}
}
}
return null;
}
// --- PERMISSION METHODS ---
// Check if MANAGE_EXTERNAL_STORAGE is granted
public boolean hasAllFilesAccess() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return Environment.isExternalStorageManager();
}
return true; // On Android 10 and below, WRITE_EXTERNAL_STORAGE is enough
}
// Open settings page for granting MANAGE_EXTERNAL_STORAGE
public void requestAllFilesAccess() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
} catch (Exception e) {
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent);
}
}
}
// --- FOLDER METHODS ---
// Check if folder exists
public boolean folderExists(String folderName) {
try {
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
return dir.exists() && dir.isDirectory();
} catch (Exception e) {
Log.e(TAG, "Error checking folder existence: " + e.getMessage());
return false;
}
}
// Create folder
public boolean createFolder(String folderName) {
try {
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
if (!dir.exists()) {
if (!dir.mkdirs()) return false;
}
return true;
} catch (Exception e) {
Log.e(TAG, "createFolder failed: " + e.getMessage());
return false;
}
}
// Delete folder recursively
public boolean deleteFolder(String folderName) {
try {
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
if (!dir.exists() || !dir.isDirectory()) return false;
return deleteRecursive(dir);
} catch (Exception e) {
Log.e(TAG, "deleteFolder failed: " + e.getMessage());
return false;
}
}
private boolean deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
File[] children = fileOrDirectory.listFiles();
if (children != null) {
for (File child : children) deleteRecursive(child);
}
}
return fileOrDirectory.delete();
}
// --- FILE METHODS ---
// Check if file exists AND is readable
public boolean fileExists(String folderName, String fileName) {
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
boolean exists = file.exists() && file.isFile();
boolean readable = exists && file.canRead();
Log.i(TAG, "fileExists: " + folderName + "/" + fileName + " - exists: " + exists + ", readable: " + readable);
return exists && readable;
} catch (Exception e) {
Log.e(TAG, "fileExists failed: " + e.getMessage());
return false;
}
}
// Create or overwrite file
public boolean createFile(String folderName, String fileName, String content) {
try {
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
if (!dir.exists() && !dir.mkdirs()) return false;
File file = new File(dir, fileName);
try (FileOutputStream fos = new FileOutputStream(file)) {
if (content != null) fos.write(content.getBytes("UTF-8"));
fos.flush();
}
return true;
} catch (Exception e) {
Log.e(TAG, "createFile failed: " + e.getMessage());
return false;
}
}
// Read entire file
public String readFile(String folderName, String fileName) {
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!file.exists() || !file.canRead()) return "";
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
}
return sb.toString().trim();
} catch (Exception e) {
Log.e(TAG, "readFile failed: " + e.getMessage());
return "";
}
}
// Append to file
public boolean appendToFile(String folderName, String fileName, String content) {
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!file.exists()) return createFile(folderName, fileName, content);
try (FileOutputStream fos = new FileOutputStream(file, true)) {
if (content != null) fos.write(content.getBytes("UTF-8"));
fos.flush();
}
return true;
} catch (Exception e) {
Log.e(TAG, "appendToFile failed: " + e.getMessage());
return false;
}
}
// Delete file
public boolean deleteFile(String folderName, String fileName) {
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!file.exists()) return false;
return file.delete();
} catch (Exception e) {
Log.e(TAG, "deleteFile failed: " + e.getMessage());
return false;
}
}
// Read a specific line
public String readLineFromFile(String folderName, String fileName, int lineNumber) {
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!file.exists() || !file.canRead()) return "";
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
String line;
int count = 0;
while ((line = reader.readLine()) != null) {
if (count == lineNumber) {
return line;
}
count++;
}
}
} catch (Exception e) {
Log.e(TAG, "readLineFromFile failed: " + e.getMessage());
}
return "";
}
// Count number of lines
public int getNumberOfLines(String folderName, String fileName) {
int count = 0;
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!file.exists() || !file.canRead()) return 0;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
while (reader.readLine() != null) count++;
}
} catch (Exception e) {
Log.e(TAG, "getNumberOfLines failed: " + e.getMessage());
}
return count;
}
// List files in folder (newline-separated)
public String listFilesInFolder(String folderName) {
try {
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
if (!dir.exists() || !dir.isDirectory()) return "";
StringBuilder sb = new StringBuilder();
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
sb.append(f.getName());
if (f.isDirectory()) sb.append(" (dir)");
sb.append("\n");
}
}
return sb.toString().trim();
} catch (Exception e) {
Log.e(TAG, "listFilesInFolder failed: " + e.getMessage());
return "";
}
}
// Copy internal -> external
public boolean copyFileToExternal(String internalRelativePath, String folderName, String fileName) {
try {
File src = new File(getFilesDir(), internalRelativePath);
if (!src.exists()) return false;
File dst = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!dst.getParentFile().exists()) dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
}
return true;
} catch (Exception e) {
Log.e(TAG, "copyFileToExternal failed: " + e.getMessage());
return false;
}
}
// Copy external -> internal
public boolean copyFileFromExternal(String folderName, String fileName, String internalRelativePath) {
try {
File src = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!src.exists() || !src.canRead()) return false;
File dst = new File(getFilesDir(), internalRelativePath);
if (!dst.getParentFile().exists()) dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
}
return true;
} catch (Exception e) {
Log.e(TAG, "copyFileFromExternal failed: " + e.getMessage());
return false;
}
}
// --- Create folder on SD card ---
public boolean createFolderOnSDCard(String folderName) {
File sdRoot = getExternalSdCard();
if (sdRoot == null) return false;
File folder = new File(sdRoot, folderName);
return folder.exists() || folder.mkdirs();
}
// --- Create/overwrite file on SD card ---
public boolean createFileOnSDCard(String folderName, String fileName, String content) {
File sdRoot = getExternalSdCard();
if (sdRoot == null) return false;
try {
File folder = new File(sdRoot, folderName);
if (!folder.exists()) folder.mkdirs();
File file = new File(folder, fileName);
try (FileOutputStream fos = new FileOutputStream(file, false)) {
fos.write(content.getBytes("UTF-8"));
fos.flush();
}
return true;
} catch (Exception e) {
Log.e(TAG, "Error writing file to SD card", e);
return false;
}
}
// --- Read file from SD card ---
public String readFileFromSDCard(String folderName, String fileName) {
File sdRoot = getExternalSdCard();
if (sdRoot == null) return "";
try {
File file = new File(new File(sdRoot, folderName), fileName);
if (!file.exists() || !file.canRead()) return "";
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
}
return sb.toString().trim();
} catch (Exception e) {
Log.e(TAG, "Error reading file from SD card", e);
return "";
}
}
// --- Delete file from SD card ---
public boolean deleteFileFromSDCard(String folderName, String fileName) {
File sdRoot = getExternalSdCard();
if (sdRoot == null) return false;
File file = new File(new File(sdRoot, folderName), fileName);
return file.exists() && file.delete();
}
// --- SD CARD OPERATIONS ---
// Check if folder exists on SD card
public boolean folderExistsOnSDCard(String folderName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File dir = new File(sdCard, folderName);
return dir.exists() && dir.isDirectory();
}
// Recursive delete helper for SD card
private boolean deleteRecursiveFromSDCard(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
File[] children = fileOrDirectory.listFiles();
if (children != null) {
for (File child : children) {
deleteRecursiveFromSDCard(child);
}
}
}
return fileOrDirectory.delete();
}
// Delete folder from SD card
public boolean deleteFolderFromSDCard(String folderName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File dir = new File(sdCard, folderName);
if (dir.exists() && dir.isDirectory()) {
return deleteRecursiveFromSDCard(dir);
}
return false;
}
// Check if a file exists on SD card
public boolean fileExistsOnSDCard(String folderName, String fileName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File file = new File(new File(sdCard, folderName), fileName);
boolean exists = file.exists() && file.isFile();
boolean readable = exists && file.canRead();
Log.i(TAG, "fileExistsOnSDCard: " + folderName + "/" + fileName + " - exists: " + exists + ", readable: " + readable);
return exists && readable;
}
// Append to file on SD card
public boolean appendToFileOnSDCard(String folderName, String fileName, String content) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File dir = new File(sdCard, folderName);
if (!dir.exists()) dir.mkdirs();
File file = new File(dir, fileName);
try (FileOutputStream fos = new FileOutputStream(file, true)) {
fos.write(content.getBytes("UTF-8"));
fos.flush();
return true;
} catch (Exception e) {
Log.e(TAG, "appendToFileOnSDCard failed", e);
return false;
}
}
// Read a specific line from a file on SD card
public String readLineFromFileOnSDCard(String folderName, String fileName, int lineNumber) {
File sdCard = getExternalSdCard();
if (sdCard == null) return "";
File file = new File(new File(sdCard, folderName), fileName);
if (!file.exists() || !file.canRead()) return "";
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
String line;
int count = 0;
while ((line = reader.readLine()) != null) {
if (count == lineNumber) return line;
count++;
}
} catch (Exception e) {
Log.e(TAG, "readLineFromFileOnSDCard failed", e);
}
return "";
}
// Get number of lines in a file on SD card
public int getNumberOfLinesOnSDCard(String folderName, String fileName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return 0;
File file = new File(new File(sdCard, folderName), fileName);
if (!file.exists() || !file.canRead()) return 0;
int count = 0;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
while (reader.readLine() != null) count++;
} catch (Exception e) {
Log.e(TAG, "getNumberOfLinesOnSDCard failed", e);
}
return count;
}
// List files in a folder on SD card
public String listFilesInFolderOnSDCard(String folderName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return "";
File dir = new File(sdCard, folderName);
if (!dir.exists() || !dir.isDirectory()) return "";
StringBuilder sb = new StringBuilder();
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
sb.append(f.getName());
if (f.isDirectory()) sb.append(" (dir)");
sb.append("\n");
}
}
return sb.toString().trim();
}
// Copy file from internal storage -> SD card
public boolean copyFileToSDCard(String internalRelativePath, String folderName, String fileName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File src = new File(getFilesDir(), internalRelativePath);
if (!src.exists()) return false;
File dst = new File(new File(sdCard, folderName), fileName);
dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
return true;
} catch (Exception e) {
Log.e(TAG, "copyFileToSDCard failed", e);
return false;
}
}
// Copy file from SD card -> internal storage
public boolean copyFileFromSDCard(String folderName, String fileName, String internalRelativePath) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File src = new File(new File(sdCard, folderName), fileName);
if (!src.exists() || !src.canRead()) return false;
File dst = new File(getFilesDir(), internalRelativePath);
dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
return true;
} catch (Exception e) {
Log.e(TAG, "copyFileFromSDCard failed", e);
return false;
}
}
// Copy file from External (primary shared storage) -> SD card
public boolean copyFromExternalToSDCard(String externalFolder, String externalFile, String sdFolder, String sdFile) {
File external = Environment.getExternalStorageDirectory();
if (external == null) return false;
File src = new File(new File(external, externalFolder), externalFile);
if (!src.exists() || !src.canRead()) return false;
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File dst = new File(new File(sdCard, sdFolder), sdFile);
dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
return true;
} catch (Exception e) {
Log.e(TAG, "copyFromExternalToSDCard failed", e);
return false;
}
}
// Copy file from SD card -> External (primary shared storage)
public boolean copyFromSDCardToExternal(String sdFolder, String sdFile, String externalFolder, String externalFile) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File src = new File(new File(sdCard, sdFolder), sdFile);
if (!src.exists() || !src.canRead()) return false;
File external = Environment.getExternalStorageDirectory();
if (external == null) return false;
File dst = new File(new File(external, externalFolder), externalFile);
dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
return true;
} catch (Exception e) {
Log.e(TAG, "copyFromSDCardToExternal failed", e);
return false;
}
}
// ================================================================
// 📸 CAMERA CAPTURE (Complete Section)
// ================================================================
private String lastPhotoPath;
// Camera permission methods
public boolean hasCameraPermission() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}
public void requestCameraPermission() {
if (!hasCameraPermission()) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
2002
);
}
}
// Main camera method
public String openCamera() {
try {
// Check camera permission first
if (!hasCameraPermission()) {
Log.w(TAG, "Camera permission not granted");
return "camera_permission_denied";
}
// Check storage permission for saving photos
if (!hasAllFilesAccess()) {
Log.w(TAG, "Storage permission not granted");
return "storage_permission_denied";
}
// Use Pictures directory that Hollywood can access
File storageDir = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"HollywoodPhotos"
);
if (!storageDir.exists()) storageDir.mkdirs();
// Create unique filename
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String fileName = "photo_" + timeStamp + ".jpg";
File photoFile = new File(storageDir, fileName);
lastPhotoPath = photoFile.getAbsolutePath();
Log.i(TAG, "Photo will be saved to: " + lastPhotoPath);
// Use MediaStore to create the file entry
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.DATA, lastPhotoPath);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/HollywoodPhotos");
values.put(MediaStore.Images.Media.IS_PENDING, 1);
}
Uri imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
if (imageUri != null) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, 2001);
Log.i(TAG, "Camera intent started");
return "camera_opened";
} else {
Log.e(TAG, "No camera app available");
lastPhotoPath = "";
return "no_camera_app";
}
} else {
Log.e(TAG, "Failed to create MediaStore entry");
lastPhotoPath = "";
return "media_store_error";
}
} catch (Exception e) {
Log.e(TAG, "openCamera failed: " + e.toString());
lastPhotoPath = "";
return "error: " + e.getMessage();
}
}
// Handle camera result
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 2001) {
if (resultCode == RESULT_OK) {
Log.i(TAG, "Camera returned OK, photo path: " + lastPhotoPath);
// Finalize MediaStore entry for Android Q+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && lastPhotoPath != null) {
try {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.IS_PENDING, 0);
String selection = MediaStore.Images.Media.DATA + "=?";
String[] selectionArgs = new String[]{lastPhotoPath};
getContentResolver().update(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
values, selection, selectionArgs);
Log.i(TAG, "MediaStore entry finalized for: " + lastPhotoPath);
} catch (Exception e) {
Log.e(TAG, "Failed to finalize MediaStore entry", e);
}
}
// Trigger media scan so file is immediately visible to Hollywood
MediaScannerConnection.scanFile(this, new String[]{lastPhotoPath},
new String[]{"image/jpeg"}, null);
} else {
// Camera was cancelled
Log.i(TAG, "Camera was cancelled");
lastPhotoPath = "";
// Clean up pending file if camera was cancelled
if (lastPhotoPath != null) {
try {
File cancelledFile = new File(lastPhotoPath);
if (cancelledFile.exists()) {
cancelledFile.delete();
}
} catch (Exception e) {
Log.e(TAG, "Failed to delete cancelled photo", e);
}
lastPhotoPath = "";
}
}
}
}
// Get the photo path for Hollywood
public String getLastPhotoPath() {
if (lastPhotoPath != null) {
File photoFile = new File(lastPhotoPath);
if (photoFile.exists()) {
Log.i(TAG, "Returning existing photo path: " + lastPhotoPath);
return lastPhotoPath;
} else {
Log.w(TAG, "Photo file doesn't exist: " + lastPhotoPath);
return "";
}
}
return "";
}
// Check if camera is available on the device
public boolean isCameraAvailable() {
try {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
return intent.resolveActivity(getPackageManager()) != null;
} catch (Exception e) {
Log.e(TAG, "isCameraAvailable failed", e);
return false;
}
}
// ================================================================
// 📍 GPS LOCATION (Complete Section)
// ================================================================
// Location permission methods
public boolean hasLocationPermission() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
}
public void requestLocationPermission() {
if (!hasLocationPermission()) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
2003
);
}
}
// Check if location services are enabled
public boolean isLocationEnabled() {
try {
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
if (lm == null) return false;
return lm.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception e) {
Log.e(TAG, "isLocationEnabled failed", e);
return false;
}
}
// Get last known location (quick, cached location)
public String getLastKnownLocation() {
try {
// Check location permission
if (!hasLocationPermission()) {
Log.w(TAG, "Location permission not granted");
return "permission_denied";
}
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
if (lm == null) {
Log.e(TAG, "LocationManager is null");
return "service_unavailable";
}
// Check if location services are enabled
if (!isLocationEnabled()) {
Log.w(TAG, "Location services are disabled");
return "location_disabled";
}
// Try multiple location providers in order of preference
Location location = null;
List<String> providers = lm.getProviders(true);
for (String provider : providers) {
try {
Location loc = lm.getLastKnownLocation(provider);
if (loc != null) {
if (location == null || loc.getAccuracy() < location.getAccuracy()) {
location = loc;
}
}
} catch (SecurityException e) {
Log.w(TAG, "No permission for provider: " + provider);
} catch (Exception e) {
Log.w(TAG, "Error getting location from provider: " + provider, e);
}
}
if (location != null) {
double lat = location.getLatitude();
double lon = location.getLongitude();
float accuracy = location.getAccuracy();
long time = location.getTime();
String result = String.format(Locale.US, "%.6f,%.6f", lat, lon);
Log.i(TAG, "Location found: " + result + " (accuracy: " + accuracy + "m, time: " + time + ")");
return result;
} else {
Log.w(TAG, "No last known location available");
return "no_location_available";
}
} catch (SecurityException e) {
Log.e(TAG, "Location permission denied", e);
return "permission_denied";
} catch (Exception e) {
Log.e(TAG, "getLastKnownLocation failed", e);
return "error: " + e.getMessage();
}
}
// Get location with provider preference (GPS first, then network)
public String getCurrentLocation() {
try {
// Check location permission
if (!hasLocationPermission()) {
return "permission_denied";
}
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
if (lm == null) {
return "service_unavailable";
}
// Check if location services are enabled
if (!isLocationEnabled()) {
return "location_disabled";
}
// Try GPS first for highest accuracy
Location location = null;
if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
try {
location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
Log.i(TAG, "Using GPS location");
}
} catch (SecurityException e) {
Log.w(TAG, "No permission for GPS provider");
}
}
// Fallback to network if GPS not available
if (location == null && lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
try {
location = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (location != null) {
Log.i(TAG, "Using network location");
}
} catch (SecurityException e) {
Log.w(TAG, "No permission for network provider");
}
}
if (location != null) {
double lat = location.getLatitude();
double lon = location.getLongitude();
float accuracy = location.getAccuracy();
String result = String.format(Locale.US, "%.6f,%.6f", lat, lon);
Log.i(TAG, "Current location: " + result + " (accuracy: " + accuracy + "m)");
return result;
} else {
Log.w(TAG, "No current location available from any provider");
return "location_unavailable";
}
} catch (SecurityException e) {
Log.e(TAG, "Location permission denied", e);
return "permission_denied";
} catch (Exception e) {
Log.e(TAG, "getCurrentLocation failed", e);
return "error";
}
}
// Get detailed location info (returns comma-separated: lat,lon,accuracy,time,provider)
public String getDetailedLocation() {
try {
if (!hasLocationPermission()) {
return "permission_denied";
}
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
if (lm == null) return "service_unavailable";
if (!isLocationEnabled()) return "location_disabled";
Location bestLocation = null;
List<String> providers = lm.getProviders(true);
for (String provider : providers) {
try {
Location loc = lm.getLastKnownLocation(provider);
if (loc != null) {
if (bestLocation == null || loc.getAccuracy() < bestLocation.getAccuracy()) {
bestLocation = loc;
}
}
} catch (Exception e) {
// Continue with next provider
}
}
if (bestLocation != null) {
return String.format(Locale.US, "%.6f,%.6f,%.1f,%d,%s",
bestLocation.getLatitude(),
bestLocation.getLongitude(),
bestLocation.getAccuracy(),
bestLocation.getTime(),
bestLocation.getProvider()
);
} else {
return "no_location_available";
}
} catch (Exception e) {
Log.e(TAG, "getDetailedLocation failed", e);
return "error";
}
}
// Open location settings so user can enable location services
public void openLocationSettings() {
try {
Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "openLocationSettings failed", e);
// Fallback to general settings
try {
Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
startActivity(intent);
} catch (Exception e2) {
Log.e(TAG, "Fallback settings also failed", e2);
}
}
}
// ================================================================
// 💥 VIBRATION
// ================================================================
// public void vibrateDevice(int durationMs) {
// try {
// Vibrator v = (Vibrator) getSystemService(VIBRATOR_SERVICE);
// if (v != null) v.vibrate(durationMs);
// } catch (Exception e) {
// Log.e(TAG, "vibrateDevice", e);
// }
// }
public void vibrateNow(int durationMs) {
try {
HollywoodActivity.vibrateDevice(durationMs);
} catch (Exception e) {
Log.e(TAG, "vibrateNow failed", e);
}
}
// ================================================================
// 📤 SHARE TEXT
// ================================================================
public void shareText(String message) {
try {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, message);
sendIntent.setType("text/plain");
startActivity(Intent.createChooser(sendIntent, "Share via"));
} catch (Exception e) {
Log.e(TAG, "shareText", e);
}
}
// ================================================================
// 📤 SHARE IMAGE
// ================================================================
public void shareImage(String imagePath) {
try {
File imageFile = new File(imagePath);
if (!imageFile.exists()) {
Log.e(TAG, "Image file not found: " + imagePath);
return;
}
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
// Use YOUR existing FileProvider authority
Uri imageUri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
imageUri = FileProvider.getUriForFile(this,
getPackageName() + ".fileprovider", // ← Changed to match your manifest
imageFile);
} else {
imageUri = Uri.fromFile(imageFile);
}
shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(shareIntent, "Share Photo"));
Log.i(TAG, "Sharing image: " + imagePath);
} catch (Exception e) {
Log.e(TAG, "shareImage failed", e);
}
}
// ================================================================
// 📋 CLIPBOARD FUNCTIONS
// ================================================================
// Copy text to clipboard
public void copyToClipboard(String text) {
try {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Hollywood App", text);
clipboard.setPrimaryClip(clip);
Log.i(TAG, "Text copied to clipboard: " + text);
// Optional: Show a quick toast message
// Toast.makeText(this, "Copied to clipboard", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Log.e(TAG, "copyToClipboard failed", e);
}
}
// Get text from clipboard
public String getFromClipboard() {
try {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
if (clipboard.hasPrimaryClip()) {
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
String text = item.getText().toString();
Log.i(TAG, "Retrieved from clipboard: " + text);
return text;
}
} catch (Exception e) {
Log.e(TAG, "getFromClipboard failed", e);
}
return "";
}
// ================================================================
// 🔆 KEEP SCREEN ON
// ================================================================
private boolean screenShouldStayOn = false;
// Keep screen on - call this repeatedly if needed
public void keepScreenOn() {
try {
runOnUiThread(new Runnable() {
@Override
public void run() {
getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
screenShouldStayOn = true;
Log.i(TAG, "Screen keep-on flag set");
}
});
} catch (Exception e) {
Log.e(TAG, "keepScreenOn failed", e);
}
}
// Allow screen to turn off normally
public void allowScreenOff() {
try {
runOnUiThread(new Runnable() {
@Override
public void run() {
getWindow().clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
screenShouldStayOn = false;
Log.i(TAG, "Screen keep-on flag cleared");
}
});
} catch (Exception e) {
Log.e(TAG, "allowScreenOff failed", e);
}
}
// Force screen on and wake up device (requires WAKE_LOCK permission)
public void wakeUpScreen() {
try {
// This is more aggressive - wakes up the screen if it's off
android.view.Window window = getWindow();
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Log.i(TAG, "Screen woken up and kept on");
} catch (Exception e) {
Log.e(TAG, "wakeUpScreen failed", e);
}
}
// Check if screen is currently kept on
public boolean isScreenKeptOn() {
try {
int flags = getWindow().getAttributes().flags;
return (flags & android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0;
} catch (Exception e) {
Log.e(TAG, "isScreenKeptOn failed", e);
return false;
}
}
// ================================================================
// 💡 LIGHT SENSOR FUNCTIONS
// ================================================================
private SensorManager sensorManager;
private Sensor lightSensor;
private float lastLightValue = -1;
private SensorEventListener lightSensorListener;
// Check if device has a light sensor
public boolean hasLightSensor() {
try {
if (sensorManager == null) {
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
}
if (sensorManager != null) {
lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
return lightSensor != null;
}
return false;
} catch (Exception e) {
Log.e(TAG, "hasLightSensor failed", e);
return false;
}
}
// Get current light level (in lux) - returns -1 if not available
public float getLightLevel() {
return lastLightValue;
}
// Start listening to light sensor updates
public boolean startLightSensor() {
try {
if (!hasLightSensor()) {
Log.w(TAG, "No light sensor available");
return false;
}
if (lightSensorListener == null) {
lightSensorListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_LIGHT) {
lastLightValue = event.values[0];
Log.d(TAG, "Light level: " + lastLightValue + " lux");
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Handle accuracy changes if needed
}
};
}
// Register listener with normal delay
sensorManager.registerListener(lightSensorListener, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
Log.i(TAG, "Light sensor started");
return true;
} catch (Exception e) {
Log.e(TAG, "startLightSensor failed", e);
return false;
}
}
// Stop listening to light sensor
public void stopLightSensor() {
try {
if (sensorManager != null && lightSensorListener != null) {
sensorManager.unregisterListener(lightSensorListener);
Log.i(TAG, "Light sensor stopped");
}
} catch (Exception e) {
Log.e(TAG, "stopLightSensor failed", e);
}
}
// Get light sensor information
public String getLightSensorInfo() {
try {
if (!hasLightSensor()) {
return "No light sensor available";
}
StringBuilder info = new StringBuilder();
info.append("Light Sensor Info:\n");
info.append("Name: ").append(lightSensor.getName()).append("\n");
info.append("Vendor: ").append(lightSensor.getVendor()).append("\n");
info.append("Version: ").append(lightSensor.getVersion()).append("\n");
info.append("Max Range: ").append(lightSensor.getMaximumRange()).append(" lux\n");
info.append("Resolution: ").append(lightSensor.getResolution()).append(" lux\n");
info.append("Power: ").append(lightSensor.getPower()).append(" mA\n");
info.append("Current Value: ").append(lastLightValue).append(" lux");
return info.toString();
} catch (Exception e) {
Log.e(TAG, "getLightSensorInfo failed", e);
return "Error getting sensor info";
}
}
// Get light level as human-readable description
public String getLightDescription() {
if (lastLightValue < 0) return "Not available";
if (lastLightValue < 10) return "Very Dark";
else if (lastLightValue < 50) return "Dark";
else if (lastLightValue < 200) return "Dim";
else if (lastLightValue < 1000) return "Normal";
else if (lastLightValue < 5000) return "Bright";
else return "Very Bright";
}
}I did not have the time to work with it at the moment, so I just gave it a test run so I know that it does work.
Code: Select all
; Light sensor example
SetFont(#SANS, 36)
Wait(50)
Function TestLightSensor()
; Check if device has light sensor
hasSensor = CallJavaMethod("hasLightSensor", {ReturnType=#BOOLEAN})
If hasSensor
NPrint("Light sensor available!")
; Get sensor info
sensorInfo$ = CallJavaMethod("getLightSensorInfo", {ReturnType=#STRING})
NPrint(sensorInfo$)
; Start monitoring
CallJavaMethod("startLightSensor", {ReturnType=#BOOLEAN})
NPrint("Light sensor started...")
; Read values for a while
For k = 1 To 10
Wait(50) ; Wait 1 second
lightLevel = CallJavaMethod("getLightLevel", {ReturnType=#FLOAT})
lightDesc$ = CallJavaMethod("getLightDescription", {ReturnType=#STRING})
NPrint("Light: " .. lightLevel .. " lux (" .. lightDesc$ .. ")")
Next
; Stop monitoring
CallJavaMethod("stopLightSensor", {ReturnType=#VOID})
NPrint("Light sensor stopped")
Else
NPrint("No light sensor available on this device")
EndIf
EndFunction
; Simple one-time reading
Function GetCurrentLightLevel()
hasSensor = CallJavaMethod("hasLightSensor", {ReturnType=#BOOLEAN})
If hasSensor
; Start sensor briefly
CallJavaMethod("startLightSensor", {ReturnType=#BOOLEAN})
Wait(25) ; Wait for reading
lightLevel = CallJavaMethod("getLightLevel", {ReturnType=#FLOAT})
CallJavaMethod("stopLightSensor", {ReturnType=#VOID})
Return(lightLevel)
Else
Return(-1)
EndIf
EndFunction
; Test it
For i = 1 To 15
NPrint("=== LIGHT SENSOR TEST ===")
TestLightSensor()
Wait(50)
Next
WaitLeftMouse
End
-
xabierpayet
- Posts: 280
- Joined: Fri Feb 24, 2012 9:34 am
Re: Scoped storage methods for Android 10+
oh, really nice, i must test it this weekend
thanks mate
thanks mate
Re: Scoped storage methods for Android 10+
I updated the files a bit, now the java file have added support for multiple type of sensors.
HollywoodDelegate.java file: (As always, change your package name in the java file to match your package name in apk compiler settings)
Then I changed the hollywood script accordingly. I also made hollywood save the listing of all available sensors into a file sensors.txt
This will require permissions and the corresponing setup in the manifest xml
Hollywood script:
My current custom androidmanifest.xml:
HollywoodDelegate.java file: (As always, change your package name in the java file to match your package name in apk compiler settings)
Code: Select all
package com.amy66dev.extended;
// Core Android functionality
import android.content.Intent; // Camera, GPS, Sharing, Permissions
import android.net.Uri; // Camera, Sharing, File operations
import android.os.Build; // Version checks throughout
import android.os.Environment; // File operations, SD card, Storage
import android.util.Log; // Logging throughout
import android.app.Activity; // Base class, context
// Permissions and hardware features
import android.Manifest; // Camera, Location permissions
import android.content.pm.PackageManager; // Permission checking
import android.location.Location; // GPS functionality
import android.location.LocationManager; // GPS functionality
import android.media.MediaScannerConnection; // Camera file scanning
import android.os.Vibrator; // Vibration (currently commented out)
import android.provider.MediaStore; // Camera, MediaStore operations
import android.widget.Toast; // Toast messages (optional)
import android.content.Context; // System services
import android.content.ContentValues; // MediaStore operations
import android.media.MediaScannerConnection; // Camera file scanning (duplicate)
import android.content.ClipboardManager; // Clipboard functions
import android.content.ClipData; // Clipboard functions
import android.view.WindowManager; // Keep Screen On
// AndroidX libraries
import androidx.annotation.Nullable; // Nullable annotations
import androidx.core.app.ActivityCompat; // Permission requests
import androidx.core.content.ContextCompat; // Permission checking
import androidx.core.content.FileProvider; // File sharing
// Java utilities
import java.text.SimpleDateFormat; // Camera timestamp
import java.util.Date; // Camera timestamp
import java.util.Locale; // String formatting
import java.util.List; // GPS location providers
import java.util.ArrayList; // Multiple permissions requests
// File I/O operations
import java.io.File; // All file operations
import java.io.FileInputStream; // File reading, copy operations
import java.io.FileOutputStream; // File writing, copy operations
import java.io.IOException; // Exception handling
import java.io.BufferedReader; // File reading
import java.io.InputStreamReader; // File reading
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import java.util.Map;
import java.util.HashMap;
/**
* HollywoodDelegate - Direct external storage file utilities.
* Must be included as Custom Code in the Hollywood APK Compiler.
*/
public class HollywoodDelegate extends HollywoodActivity {
private static final String TAG = "HollywoodDelegate";
private SensorManager sensorManager;
// --- Add SD card helper HERE (inside the class, not before imports)
private File getExternalSdCard() {
File[] externalDirs = getApplicationContext().getExternalFilesDirs(null);
if (externalDirs != null && externalDirs.length > 1) {
for (File dir : externalDirs) {
if (dir != null && Environment.isExternalStorageRemovable(dir)) {
return dir.getParentFile().getParentFile().getParentFile().getParentFile();
}
}
}
return null;
}
// --- PERMISSION METHODS ---
// Check if MANAGE_EXTERNAL_STORAGE is granted
public boolean hasAllFilesAccess() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return Environment.isExternalStorageManager();
}
return true; // On Android 10 and below, WRITE_EXTERNAL_STORAGE is enough
}
// Open settings page for granting MANAGE_EXTERNAL_STORAGE
public void requestAllFilesAccess() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
} catch (Exception e) {
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent);
}
}
}
// --- FOLDER METHODS ---
// Check if folder exists
public boolean folderExists(String folderName) {
try {
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
return dir.exists() && dir.isDirectory();
} catch (Exception e) {
Log.e(TAG, "Error checking folder existence: " + e.getMessage());
return false;
}
}
// Create folder
public boolean createFolder(String folderName) {
try {
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
if (!dir.exists()) {
if (!dir.mkdirs()) return false;
}
return true;
} catch (Exception e) {
Log.e(TAG, "createFolder failed: " + e.getMessage());
return false;
}
}
// Delete folder recursively
public boolean deleteFolder(String folderName) {
try {
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
if (!dir.exists() || !dir.isDirectory()) return false;
return deleteRecursive(dir);
} catch (Exception e) {
Log.e(TAG, "deleteFolder failed: " + e.getMessage());
return false;
}
}
private boolean deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
File[] children = fileOrDirectory.listFiles();
if (children != null) {
for (File child : children) deleteRecursive(child);
}
}
return fileOrDirectory.delete();
}
// --- FILE METHODS ---
// Check if file exists AND is readable
public boolean fileExists(String folderName, String fileName) {
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
boolean exists = file.exists() && file.isFile();
boolean readable = exists && file.canRead();
Log.i(TAG, "fileExists: " + folderName + "/" + fileName + " - exists: " + exists + ", readable: " + readable);
return exists && readable;
} catch (Exception e) {
Log.e(TAG, "fileExists failed: " + e.getMessage());
return false;
}
}
// Create or overwrite file
public boolean createFile(String folderName, String fileName, String content) {
try {
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
if (!dir.exists() && !dir.mkdirs()) return false;
File file = new File(dir, fileName);
try (FileOutputStream fos = new FileOutputStream(file)) {
if (content != null) fos.write(content.getBytes("UTF-8"));
fos.flush();
}
return true;
} catch (Exception e) {
Log.e(TAG, "createFile failed: " + e.getMessage());
return false;
}
}
// Read entire file
public String readFile(String folderName, String fileName) {
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!file.exists() || !file.canRead()) return "";
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
}
return sb.toString().trim();
} catch (Exception e) {
Log.e(TAG, "readFile failed: " + e.getMessage());
return "";
}
}
// Append to file
public boolean appendToFile(String folderName, String fileName, String content) {
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!file.exists()) return createFile(folderName, fileName, content);
try (FileOutputStream fos = new FileOutputStream(file, true)) {
if (content != null) fos.write(content.getBytes("UTF-8"));
fos.flush();
}
return true;
} catch (Exception e) {
Log.e(TAG, "appendToFile failed: " + e.getMessage());
return false;
}
}
// Delete file
public boolean deleteFile(String folderName, String fileName) {
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!file.exists()) return false;
return file.delete();
} catch (Exception e) {
Log.e(TAG, "deleteFile failed: " + e.getMessage());
return false;
}
}
// Read a specific line
public String readLineFromFile(String folderName, String fileName, int lineNumber) {
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!file.exists() || !file.canRead()) return "";
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
String line;
int count = 0;
while ((line = reader.readLine()) != null) {
if (count == lineNumber) {
return line;
}
count++;
}
}
} catch (Exception e) {
Log.e(TAG, "readLineFromFile failed: " + e.getMessage());
}
return "";
}
// Count number of lines
public int getNumberOfLines(String folderName, String fileName) {
int count = 0;
try {
File file = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!file.exists() || !file.canRead()) return 0;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
while (reader.readLine() != null) count++;
}
} catch (Exception e) {
Log.e(TAG, "getNumberOfLines failed: " + e.getMessage());
}
return count;
}
// List files in folder (newline-separated)
public String listFilesInFolder(String folderName) {
try {
File dir = new File(Environment.getExternalStorageDirectory(), folderName);
if (!dir.exists() || !dir.isDirectory()) return "";
StringBuilder sb = new StringBuilder();
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
sb.append(f.getName());
if (f.isDirectory()) sb.append(" (dir)");
sb.append("\n");
}
}
return sb.toString().trim();
} catch (Exception e) {
Log.e(TAG, "listFilesInFolder failed: " + e.getMessage());
return "";
}
}
// Copy internal -> external
public boolean copyFileToExternal(String internalRelativePath, String folderName, String fileName) {
try {
File src = new File(getFilesDir(), internalRelativePath);
if (!src.exists()) return false;
File dst = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!dst.getParentFile().exists()) dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
}
return true;
} catch (Exception e) {
Log.e(TAG, "copyFileToExternal failed: " + e.getMessage());
return false;
}
}
// Copy external -> internal
public boolean copyFileFromExternal(String folderName, String fileName, String internalRelativePath) {
try {
File src = new File(new File(Environment.getExternalStorageDirectory(), folderName), fileName);
if (!src.exists() || !src.canRead()) return false;
File dst = new File(getFilesDir(), internalRelativePath);
if (!dst.getParentFile().exists()) dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
}
return true;
} catch (Exception e) {
Log.e(TAG, "copyFileFromExternal failed: " + e.getMessage());
return false;
}
}
// --- Create folder on SD card ---
public boolean createFolderOnSDCard(String folderName) {
File sdRoot = getExternalSdCard();
if (sdRoot == null) return false;
File folder = new File(sdRoot, folderName);
return folder.exists() || folder.mkdirs();
}
// --- Create/overwrite file on SD card ---
public boolean createFileOnSDCard(String folderName, String fileName, String content) {
File sdRoot = getExternalSdCard();
if (sdRoot == null) return false;
try {
File folder = new File(sdRoot, folderName);
if (!folder.exists()) folder.mkdirs();
File file = new File(folder, fileName);
try (FileOutputStream fos = new FileOutputStream(file, false)) {
fos.write(content.getBytes("UTF-8"));
fos.flush();
}
return true;
} catch (Exception e) {
Log.e(TAG, "Error writing file to SD card", e);
return false;
}
}
// --- Read file from SD card ---
public String readFileFromSDCard(String folderName, String fileName) {
File sdRoot = getExternalSdCard();
if (sdRoot == null) return "";
try {
File file = new File(new File(sdRoot, folderName), fileName);
if (!file.exists() || !file.canRead()) return "";
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
}
return sb.toString().trim();
} catch (Exception e) {
Log.e(TAG, "Error reading file from SD card", e);
return "";
}
}
// --- Delete file from SD card ---
public boolean deleteFileFromSDCard(String folderName, String fileName) {
File sdRoot = getExternalSdCard();
if (sdRoot == null) return false;
File file = new File(new File(sdRoot, folderName), fileName);
return file.exists() && file.delete();
}
// --- SD CARD OPERATIONS ---
// Check if folder exists on SD card
public boolean folderExistsOnSDCard(String folderName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File dir = new File(sdCard, folderName);
return dir.exists() && dir.isDirectory();
}
// Recursive delete helper for SD card
private boolean deleteRecursiveFromSDCard(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
File[] children = fileOrDirectory.listFiles();
if (children != null) {
for (File child : children) {
deleteRecursiveFromSDCard(child);
}
}
}
return fileOrDirectory.delete();
}
// Delete folder from SD card
public boolean deleteFolderFromSDCard(String folderName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File dir = new File(sdCard, folderName);
if (dir.exists() && dir.isDirectory()) {
return deleteRecursiveFromSDCard(dir);
}
return false;
}
// Check if a file exists on SD card
public boolean fileExistsOnSDCard(String folderName, String fileName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File file = new File(new File(sdCard, folderName), fileName);
boolean exists = file.exists() && file.isFile();
boolean readable = exists && file.canRead();
Log.i(TAG, "fileExistsOnSDCard: " + folderName + "/" + fileName + " - exists: " + exists + ", readable: " + readable);
return exists && readable;
}
// Append to file on SD card
public boolean appendToFileOnSDCard(String folderName, String fileName, String content) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File dir = new File(sdCard, folderName);
if (!dir.exists()) dir.mkdirs();
File file = new File(dir, fileName);
try (FileOutputStream fos = new FileOutputStream(file, true)) {
fos.write(content.getBytes("UTF-8"));
fos.flush();
return true;
} catch (Exception e) {
Log.e(TAG, "appendToFileOnSDCard failed", e);
return false;
}
}
// Read a specific line from a file on SD card
public String readLineFromFileOnSDCard(String folderName, String fileName, int lineNumber) {
File sdCard = getExternalSdCard();
if (sdCard == null) return "";
File file = new File(new File(sdCard, folderName), fileName);
if (!file.exists() || !file.canRead()) return "";
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
String line;
int count = 0;
while ((line = reader.readLine()) != null) {
if (count == lineNumber) return line;
count++;
}
} catch (Exception e) {
Log.e(TAG, "readLineFromFileOnSDCard failed", e);
}
return "";
}
// Get number of lines in a file on SD card
public int getNumberOfLinesOnSDCard(String folderName, String fileName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return 0;
File file = new File(new File(sdCard, folderName), fileName);
if (!file.exists() || !file.canRead()) return 0;
int count = 0;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
while (reader.readLine() != null) count++;
} catch (Exception e) {
Log.e(TAG, "getNumberOfLinesOnSDCard failed", e);
}
return count;
}
// List files in a folder on SD card
public String listFilesInFolderOnSDCard(String folderName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return "";
File dir = new File(sdCard, folderName);
if (!dir.exists() || !dir.isDirectory()) return "";
StringBuilder sb = new StringBuilder();
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
sb.append(f.getName());
if (f.isDirectory()) sb.append(" (dir)");
sb.append("\n");
}
}
return sb.toString().trim();
}
// Copy file from internal storage -> SD card
public boolean copyFileToSDCard(String internalRelativePath, String folderName, String fileName) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File src = new File(getFilesDir(), internalRelativePath);
if (!src.exists()) return false;
File dst = new File(new File(sdCard, folderName), fileName);
dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
return true;
} catch (Exception e) {
Log.e(TAG, "copyFileToSDCard failed", e);
return false;
}
}
// Copy file from SD card -> internal storage
public boolean copyFileFromSDCard(String folderName, String fileName, String internalRelativePath) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File src = new File(new File(sdCard, folderName), fileName);
if (!src.exists() || !src.canRead()) return false;
File dst = new File(getFilesDir(), internalRelativePath);
dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
return true;
} catch (Exception e) {
Log.e(TAG, "copyFileFromSDCard failed", e);
return false;
}
}
// Copy file from External (primary shared storage) -> SD card
public boolean copyFromExternalToSDCard(String externalFolder, String externalFile, String sdFolder, String sdFile) {
File external = Environment.getExternalStorageDirectory();
if (external == null) return false;
File src = new File(new File(external, externalFolder), externalFile);
if (!src.exists() || !src.canRead()) return false;
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File dst = new File(new File(sdCard, sdFolder), sdFile);
dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
return true;
} catch (Exception e) {
Log.e(TAG, "copyFromExternalToSDCard failed", e);
return false;
}
}
// Copy file from SD card -> External (primary shared storage)
public boolean copyFromSDCardToExternal(String sdFolder, String sdFile, String externalFolder, String externalFile) {
File sdCard = getExternalSdCard();
if (sdCard == null) return false;
File src = new File(new File(sdCard, sdFolder), sdFile);
if (!src.exists() || !src.canRead()) return false;
File external = Environment.getExternalStorageDirectory();
if (external == null) return false;
File dst = new File(new File(external, externalFolder), externalFile);
dst.getParentFile().mkdirs();
try (FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) > 0) out.write(buf, 0, r);
out.flush();
return true;
} catch (Exception e) {
Log.e(TAG, "copyFromSDCardToExternal failed", e);
return false;
}
}
// ================================================================
// 📸 CAMERA CAPTURE (Complete Section)
// ================================================================
private String lastPhotoPath;
// Camera permission methods
public boolean hasCameraPermission() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}
public void requestCameraPermission() {
if (!hasCameraPermission()) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
2002
);
}
}
// Main camera method
public String openCamera() {
try {
// Check camera permission first
if (!hasCameraPermission()) {
Log.w(TAG, "Camera permission not granted");
return "camera_permission_denied";
}
// Check storage permission for saving photos
if (!hasAllFilesAccess()) {
Log.w(TAG, "Storage permission not granted");
return "storage_permission_denied";
}
// Use Pictures directory that Hollywood can access
File storageDir = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"HollywoodPhotos"
);
if (!storageDir.exists()) storageDir.mkdirs();
// Create unique filename
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String fileName = "photo_" + timeStamp + ".jpg";
File photoFile = new File(storageDir, fileName);
lastPhotoPath = photoFile.getAbsolutePath();
Log.i(TAG, "Photo will be saved to: " + lastPhotoPath);
// Use MediaStore to create the file entry
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.DATA, lastPhotoPath);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/HollywoodPhotos");
values.put(MediaStore.Images.Media.IS_PENDING, 1);
}
Uri imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
if (imageUri != null) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, 2001);
Log.i(TAG, "Camera intent started");
return "camera_opened";
} else {
Log.e(TAG, "No camera app available");
lastPhotoPath = "";
return "no_camera_app";
}
} else {
Log.e(TAG, "Failed to create MediaStore entry");
lastPhotoPath = "";
return "media_store_error";
}
} catch (Exception e) {
Log.e(TAG, "openCamera failed: " + e.toString());
lastPhotoPath = "";
return "error: " + e.getMessage();
}
}
// Handle camera result
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 2001) {
if (resultCode == RESULT_OK) {
Log.i(TAG, "Camera returned OK, photo path: " + lastPhotoPath);
// Finalize MediaStore entry for Android Q+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && lastPhotoPath != null) {
try {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.IS_PENDING, 0);
String selection = MediaStore.Images.Media.DATA + "=?";
String[] selectionArgs = new String[]{lastPhotoPath};
getContentResolver().update(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
values, selection, selectionArgs);
Log.i(TAG, "MediaStore entry finalized for: " + lastPhotoPath);
} catch (Exception e) {
Log.e(TAG, "Failed to finalize MediaStore entry", e);
}
}
// Trigger media scan so file is immediately visible to Hollywood
MediaScannerConnection.scanFile(this, new String[]{lastPhotoPath},
new String[]{"image/jpeg"}, null);
} else {
// Camera was cancelled
Log.i(TAG, "Camera was cancelled");
lastPhotoPath = "";
// Clean up pending file if camera was cancelled
if (lastPhotoPath != null) {
try {
File cancelledFile = new File(lastPhotoPath);
if (cancelledFile.exists()) {
cancelledFile.delete();
}
} catch (Exception e) {
Log.e(TAG, "Failed to delete cancelled photo", e);
}
lastPhotoPath = "";
}
}
}
}
// Get the photo path for Hollywood
public String getLastPhotoPath() {
if (lastPhotoPath != null) {
File photoFile = new File(lastPhotoPath);
if (photoFile.exists()) {
Log.i(TAG, "Returning existing photo path: " + lastPhotoPath);
return lastPhotoPath;
} else {
Log.w(TAG, "Photo file doesn't exist: " + lastPhotoPath);
return "";
}
}
return "";
}
// Check if camera is available on the device
public boolean isCameraAvailable() {
try {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
return intent.resolveActivity(getPackageManager()) != null;
} catch (Exception e) {
Log.e(TAG, "isCameraAvailable failed", e);
return false;
}
}
// ================================================================
// 📍 GPS LOCATION (Complete Section)
// ================================================================
// Location permission methods
public boolean hasLocationPermission() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
}
public void requestLocationPermission() {
if (!hasLocationPermission()) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
2003
);
}
}
// Check if location services are enabled
public boolean isLocationEnabled() {
try {
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
if (lm == null) return false;
return lm.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception e) {
Log.e(TAG, "isLocationEnabled failed", e);
return false;
}
}
// Get last known location (quick, cached location)
public String getLastKnownLocation() {
try {
// Check location permission
if (!hasLocationPermission()) {
Log.w(TAG, "Location permission not granted");
return "permission_denied";
}
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
if (lm == null) {
Log.e(TAG, "LocationManager is null");
return "service_unavailable";
}
// Check if location services are enabled
if (!isLocationEnabled()) {
Log.w(TAG, "Location services are disabled");
return "location_disabled";
}
// Try multiple location providers in order of preference
Location location = null;
List<String> providers = lm.getProviders(true);
for (String provider : providers) {
try {
Location loc = lm.getLastKnownLocation(provider);
if (loc != null) {
if (location == null || loc.getAccuracy() < location.getAccuracy()) {
location = loc;
}
}
} catch (SecurityException e) {
Log.w(TAG, "No permission for provider: " + provider);
} catch (Exception e) {
Log.w(TAG, "Error getting location from provider: " + provider, e);
}
}
if (location != null) {
double lat = location.getLatitude();
double lon = location.getLongitude();
float accuracy = location.getAccuracy();
long time = location.getTime();
String result = String.format(Locale.US, "%.6f,%.6f", lat, lon);
Log.i(TAG, "Location found: " + result + " (accuracy: " + accuracy + "m, time: " + time + ")");
return result;
} else {
Log.w(TAG, "No last known location available");
return "no_location_available";
}
} catch (SecurityException e) {
Log.e(TAG, "Location permission denied", e);
return "permission_denied";
} catch (Exception e) {
Log.e(TAG, "getLastKnownLocation failed", e);
return "error: " + e.getMessage();
}
}
// Get location with provider preference (GPS first, then network)
public String getCurrentLocation() {
try {
// Check location permission
if (!hasLocationPermission()) {
return "permission_denied";
}
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
if (lm == null) {
return "service_unavailable";
}
// Check if location services are enabled
if (!isLocationEnabled()) {
return "location_disabled";
}
// Try GPS first for highest accuracy
Location location = null;
if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
try {
location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
Log.i(TAG, "Using GPS location");
}
} catch (SecurityException e) {
Log.w(TAG, "No permission for GPS provider");
}
}
// Fallback to network if GPS not available
if (location == null && lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
try {
location = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (location != null) {
Log.i(TAG, "Using network location");
}
} catch (SecurityException e) {
Log.w(TAG, "No permission for network provider");
}
}
if (location != null) {
double lat = location.getLatitude();
double lon = location.getLongitude();
float accuracy = location.getAccuracy();
String result = String.format(Locale.US, "%.6f,%.6f", lat, lon);
Log.i(TAG, "Current location: " + result + " (accuracy: " + accuracy + "m)");
return result;
} else {
Log.w(TAG, "No current location available from any provider");
return "location_unavailable";
}
} catch (SecurityException e) {
Log.e(TAG, "Location permission denied", e);
return "permission_denied";
} catch (Exception e) {
Log.e(TAG, "getCurrentLocation failed", e);
return "error";
}
}
// Get detailed location info (returns comma-separated: lat,lon,accuracy,time,provider)
public String getDetailedLocation() {
try {
if (!hasLocationPermission()) {
return "permission_denied";
}
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
if (lm == null) return "service_unavailable";
if (!isLocationEnabled()) return "location_disabled";
Location bestLocation = null;
List<String> providers = lm.getProviders(true);
for (String provider : providers) {
try {
Location loc = lm.getLastKnownLocation(provider);
if (loc != null) {
if (bestLocation == null || loc.getAccuracy() < bestLocation.getAccuracy()) {
bestLocation = loc;
}
}
} catch (Exception e) {
// Continue with next provider
}
}
if (bestLocation != null) {
return String.format(Locale.US, "%.6f,%.6f,%.1f,%d,%s",
bestLocation.getLatitude(),
bestLocation.getLongitude(),
bestLocation.getAccuracy(),
bestLocation.getTime(),
bestLocation.getProvider()
);
} else {
return "no_location_available";
}
} catch (Exception e) {
Log.e(TAG, "getDetailedLocation failed", e);
return "error";
}
}
// Open location settings so user can enable location services
public void openLocationSettings() {
try {
Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "openLocationSettings failed", e);
// Fallback to general settings
try {
Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
startActivity(intent);
} catch (Exception e2) {
Log.e(TAG, "Fallback settings also failed", e2);
}
}
}
// ================================================================
// 💥 VIBRATION
// ================================================================
// public void vibrateDevice(int durationMs) {
// try {
// Vibrator v = (Vibrator) getSystemService(VIBRATOR_SERVICE);
// if (v != null) v.vibrate(durationMs);
// } catch (Exception e) {
// Log.e(TAG, "vibrateDevice", e);
// }
// }
public void vibrateNow(int durationMs) {
try {
HollywoodActivity.vibrateDevice(durationMs);
} catch (Exception e) {
Log.e(TAG, "vibrateNow failed", e);
}
}
// ================================================================
// 📤 SHARE TEXT
// ================================================================
public void shareText(String message) {
try {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, message);
sendIntent.setType("text/plain");
startActivity(Intent.createChooser(sendIntent, "Share via"));
} catch (Exception e) {
Log.e(TAG, "shareText", e);
}
}
// ================================================================
// 📤 SHARE IMAGE
// ================================================================
public void shareImage(String imagePath) {
try {
File imageFile = new File(imagePath);
if (!imageFile.exists()) {
Log.e(TAG, "Image file not found: " + imagePath);
return;
}
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
// Use YOUR existing FileProvider authority
Uri imageUri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
imageUri = FileProvider.getUriForFile(this,
getPackageName() + ".fileprovider", // ← Changed to match your manifest
imageFile);
} else {
imageUri = Uri.fromFile(imageFile);
}
shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(shareIntent, "Share Photo"));
Log.i(TAG, "Sharing image: " + imagePath);
} catch (Exception e) {
Log.e(TAG, "shareImage failed", e);
}
}
// ================================================================
// 📋 CLIPBOARD FUNCTIONS
// ================================================================
// Copy text to clipboard
public void copyToClipboard(String text) {
try {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Hollywood App", text);
clipboard.setPrimaryClip(clip);
Log.i(TAG, "Text copied to clipboard: " + text);
// Optional: Show a quick toast message
// Toast.makeText(this, "Copied to clipboard", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Log.e(TAG, "copyToClipboard failed", e);
}
}
// Get text from clipboard
public String getFromClipboard() {
try {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
if (clipboard.hasPrimaryClip()) {
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
String text = item.getText().toString();
Log.i(TAG, "Retrieved from clipboard: " + text);
return text;
}
} catch (Exception e) {
Log.e(TAG, "getFromClipboard failed", e);
}
return "";
}
// ================================================================
// 🔆 KEEP SCREEN ON
// ================================================================
private boolean screenShouldStayOn = false;
// Keep screen on - call this repeatedly if needed
public void keepScreenOn() {
try {
runOnUiThread(new Runnable() {
@Override
public void run() {
getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
screenShouldStayOn = true;
Log.i(TAG, "Screen keep-on flag set");
}
});
} catch (Exception e) {
Log.e(TAG, "keepScreenOn failed", e);
}
}
// Allow screen to turn off normally
public void allowScreenOff() {
try {
runOnUiThread(new Runnable() {
@Override
public void run() {
getWindow().clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
screenShouldStayOn = false;
Log.i(TAG, "Screen keep-on flag cleared");
}
});
} catch (Exception e) {
Log.e(TAG, "allowScreenOff failed", e);
}
}
// Force screen on and wake up device (requires WAKE_LOCK permission)
public void wakeUpScreen() {
try {
// This is more aggressive - wakes up the screen if it's off
android.view.Window window = getWindow();
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Log.i(TAG, "Screen woken up and kept on");
} catch (Exception e) {
Log.e(TAG, "wakeUpScreen failed", e);
}
}
// Check if screen is currently kept on
public boolean isScreenKeptOn() {
try {
int flags = getWindow().getAttributes().flags;
return (flags & android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0;
} catch (Exception e) {
Log.e(TAG, "isScreenKeptOn failed", e);
return false;
}
}
// ================================================================
// 📊 MULTI-SENSOR FUNCTIONS
// ================================================================
private Map<Integer, float[]> lastSensorValues = new HashMap<>();
private Map<Integer, SensorEventListener> sensorListeners = new HashMap<>();
// Check if specific sensor type is available
public boolean hasSensor(int sensorType) {
try {
if (sensorManager == null) {
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
}
if (sensorManager != null) {
Sensor sensor = sensorManager.getDefaultSensor(sensorType);
return sensor != null;
}
return false;
} catch (Exception e) {
Log.e(TAG, "hasSensor failed for type: " + sensorType, e);
return false;
}
}
// Get all available sensors
public String getAvailableSensors() {
try {
if (sensorManager == null) {
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
}
StringBuilder sb = new StringBuilder();
sb.append("Available Sensors:\n\n");
List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor : sensors) {
sb.append(sensor.getName()).append("\n");
sb.append(" Type: ").append(getSensorTypeName(sensor.getType())).append("\n");
sb.append(" Vendor: ").append(sensor.getVendor()).append("\n");
sb.append(" Power: ").append(sensor.getPower()).append(" mA\n");
sb.append(" Max Range: ").append(sensor.getMaximumRange()).append("\n");
sb.append(" Resolution: ").append(sensor.getResolution()).append("\n\n");
}
return sb.toString();
} catch (Exception e) {
Log.e(TAG, "getAvailableSensors failed", e);
return "Error getting sensor list";
}
}
// Start listening to any sensor
public boolean startSensor(int sensorType) {
try {
if (!hasSensor(sensorType)) {
Log.w(TAG, "Sensor not available: " + getSensorTypeName(sensorType));
return false;
}
Sensor sensor = sensorManager.getDefaultSensor(sensorType);
SensorEventListener listener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
lastSensorValues.put(event.sensor.getType(), event.values.clone());
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Handle accuracy changes
}
};
sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
sensorListeners.put(sensorType, listener);
Log.i(TAG, "Started sensor: " + getSensorTypeName(sensorType));
return true;
} catch (Exception e) {
Log.e(TAG, "startSensor failed for type: " + sensorType, e);
return false;
}
}
// Stop specific sensor
public void stopSensor(int sensorType) {
try {
SensorEventListener listener = sensorListeners.get(sensorType);
if (listener != null) {
sensorManager.unregisterListener(listener);
sensorListeners.remove(sensorType);
lastSensorValues.remove(sensorType);
Log.i(TAG, "Stopped sensor: " + getSensorTypeName(sensorType));
}
} catch (Exception e) {
Log.e(TAG, "stopSensor failed for type: " + sensorType, e);
}
}
// Stop all sensors
public void stopAllSensors() {
try {
for (Map.Entry<Integer, SensorEventListener> entry : sensorListeners.entrySet()) {
sensorManager.unregisterListener(entry.getValue());
}
sensorListeners.clear();
lastSensorValues.clear();
Log.i(TAG, "All sensors stopped");
} catch (Exception e) {
Log.e(TAG, "stopAllSensors failed", e);
}
}
// Get current sensor values
public String getSensorValues(int sensorType) {
try {
float[] values = lastSensorValues.get(sensorType);
if (values == null) return "No data";
StringBuilder sb = new StringBuilder();
sb.append(getSensorTypeName(sensorType)).append(": ");
for (int i = 0; i < values.length; i++) {
sb.append(String.format(Locale.US, "%.4f", values[i]));
if (i < values.length - 1) sb.append(", ");
}
return sb.toString();
} catch (Exception e) {
Log.e(TAG, "getSensorValues failed", e);
return "Error";
}
}
// Helper method to get sensor type names
private String getSensorTypeName(int sensorType) {
switch (sensorType) {
case Sensor.TYPE_ACCELEROMETER: return "Accelerometer";
case Sensor.TYPE_GYROSCOPE: return "Gyroscope";
case Sensor.TYPE_MAGNETIC_FIELD: return "Magnetometer";
case Sensor.TYPE_LIGHT: return "Light";
case Sensor.TYPE_PRESSURE: return "Pressure";
case Sensor.TYPE_PROXIMITY: return "Proximity";
case Sensor.TYPE_GRAVITY: return "Gravity";
case Sensor.TYPE_LINEAR_ACCELERATION: return "Linear Acceleration";
case Sensor.TYPE_ROTATION_VECTOR: return "Rotation Vector";
case Sensor.TYPE_RELATIVE_HUMIDITY: return "Humidity";
case Sensor.TYPE_AMBIENT_TEMPERATURE: return "Temperature";
case Sensor.TYPE_STEP_COUNTER: return "Step Counter";
case Sensor.TYPE_STEP_DETECTOR: return "Step Detector";
case Sensor.TYPE_ORIENTATION: return "Orientation";
default: return "Unknown (" + sensorType + ")";
}
}
}This will require permissions and the corresponing setup in the manifest xml
Hollywood script:
Code: Select all
; Light sensor example
@DISPLAY {Title = "Light sensor Test", ScaleMode = #SCALEMODE_AUTO, FitScale = True, SmoothScale = True, HideTitleBar = True}
; Define sensor constants (since @ENUM won't work)
Const #SENSOR_LIGHT = 5
ok = CallJavaMethod("hasAllFilesAccess", {ReturnType=#BOOLEAN})
If Not ok
NPrint("App needs ALL FILES ACCESS. Opening settings...")
CallJavaMethod("requestAllFilesAccess")
NPrint("Press to continue after granting permission")
WaitLeftMouse
EndIf
ok = CallJavaMethod("createFolder", {ReturnType=#BOOLEAN}, #STRING, "SensorList"); creates a new folder under /Downloads (Or eg. Documents if specified in java file)
Function p_TestLightSensor()
; Check if device has light sensor
hasSensor = CallJavaMethod("hasSensor", {ReturnType=#BOOLEAN}, #INTEGER, #SENSOR_LIGHT)
If hasSensor
TextOut(10, 30, "Light sensor available!")
; Get all available sensors info
sensorInfo$ = CallJavaMethod("getAvailableSensors", {ReturnType=#STRING})
; Show only light sensors on screen
lightSensorInfo$ = p_GetLightSensorInfo$(sensorInfo$)
TextOut(10, 60, lightSensorInfo$)
; Still save full list to file
ok = CallJavaMethod("createFile", {ReturnType=#BOOLEAN}, #STRING, "SensorList", #STRING, "Sensors.txt", #STRING, sensorInfo$)
; Start monitoring
CallJavaMethod("startSensor", {ReturnType=#BOOLEAN}, #INTEGER, #SENSOR_LIGHT)
TextOut(10, 300, "Light sensor started...")
; Get light values (returns string with lux value)
lightValues$ = CallJavaMethod("getSensorValues", {ReturnType=#STRING}, #INTEGER, #SENSOR_LIGHT)
;getsensorvalue returns a string formatted like "Light: xxx.0000". Removing text before converting to number
Local n = FindStr(lightvalues$, ":")
Local lv$ = LeftStr(lightvalues$, n+1)
Local lightvalues$ = ReplaceStr(lightvalues$, lv$, "")
Local lightValue = ToNumber(lightvalues$)
TextOut(10, 330, "Light: " .. lightValue .. " lux");
; Stop monitoring
CallJavaMethod("stopSensor", {ReturnType=#VOID}, #INTEGER, #SENSOR_LIGHT)
TextOut(10, 360, "Light sensor stopped")
Else
TextOut(10, 30, "No light sensor available on this device")
EndIf
EndFunction
; Get only light sensor info from the full list
Function p_GetLightSensorInfo$(sensorInfo$)
Local lines, c = SplitStr(sensorInfo$, "\n")
Local result$ = "Light Sensors:\n\n"
Local foundLight = False
For i = 1 To c
line$ = lines[i-1]
; Check if this line contains a light sensor (look for " Light" with space)
If FindStr(line$, "Light") > 0 ;<> Nil
foundLight = True
result$ = result$ .. line$ .. "\n"
; Add the next 5 lines (details)
For j = 1 To 5
If (i + j - 1) <= c
result$ = result$ .. lines[i + j - 1] .. "\n"
EndIf
Next
result$ = result$ .. "\n"
Break ; Found our light sensor, no need to continue
EndIf
Next
If Not foundLight
result$ = result$ .. "No light sensors found"
EndIf
Return(result$)
EndFunction
Function p_HandlerFunc(msg)
Switch(msg.Action)
Case "OnMouseDown":
sel = SystemRequest("Quit?", "Quit light measuring?", "Yes|No")
Switch sel
Case 1:
CallJavaMethod("stopSensor", {ReturnType=#VOID}, #INTEGER, #SENSOR_LIGHT)
Wait(10)
quitnow = True
EndSwitch
EndSwitch
EndFunction
InstallEventHandler({OnMouseDown = p_HandlerFunc})
SetFillStyle(#FILLCOLOR)
SetFont(#SANS, 24)
Repeat
CheckEvent
Box(0,0,700,600, #BLACK)
TextOut(10, 0, "=== LIGHT SENSOR TEST ===")
result$ = p_TestLightSensor()
Wait(50) ; Wait 1 second
Until quitnow
Code: Select all
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="%PACKAGE%">
<uses-feature android:glEsVersion="0x00020000" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET"/>
<application android:icon="@drawable/ic_launcher" android:label="@string/app_name">
<!-- NB: for some reason Lint returns a wrong "MissingClass" for both HollywoodDelegate and
FileRequest class so we disable checking for this error. As soon as this problem is
fixed in Lint, we should remove 'tools:ignore="MissingClass"' again because obviously
we want Lint to do its checks but as of September 2021, this seems to be broken -->
<activity android:name=".HollywoodDelegate"
android:label="@string/app_name"
android:configChanges="orientation|screenSize"
android:exported="true"
android:theme="@style/%THEME%"
tools:ignore="MissingClass">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".FileRequest" android:configChanges="orientation|screenSize" android:theme="@style/%THEME%" tools:ignore="MissingClass"/>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->
Re: Scoped storage methods for Android 10+
I notice in my own sensors.txt file the light sensor is listed 3 times, like this:
TCS3701 Light
Type: Light
Vendor: AMS, Inc.
Power: 0.75 mA
Max Range: 60000.0
Resolution: 1.0
TCS3701 Light CCT
Type: Unknown (65587)
Vendor: AMS, Inc.
Power: 0.75 mA
Max Range: 60000.0
Resolution: 1.0
Light Seamless Sensor
Type: Unknown (65614)
Vendor: Samsung Inc.
Power: 0.75 mA
Max Range: 60000.0
Resolution: 1.0
What I learned is that the first listing is the standard light sensor, used to auto correct display brightness etc, reporting lux values. The one labeled CCT also detects the color components of the light. The third is a virtual sensor, where seamless indicates that this is used for battery saving. The android system polls the hardware sensor and updates thius seamless sensor when there are changes, and the user apps can poll this seemless sensor without using additional battery to access the hardware sensor directly.
In my case the first to apear in my sensor list is the one reporting the lux values, and the sensorInfo information in my script breaks after the first listing. On other hardware the list may be sorted different, so this may need to be considered in the hollywood script.
Other sensor info that might be useful. I just paste it here for reference.
Most used sensors for possible usage in hollywood:
All sensors, also deprecated ones:
TCS3701 Light
Type: Light
Vendor: AMS, Inc.
Power: 0.75 mA
Max Range: 60000.0
Resolution: 1.0
TCS3701 Light CCT
Type: Unknown (65587)
Vendor: AMS, Inc.
Power: 0.75 mA
Max Range: 60000.0
Resolution: 1.0
Light Seamless Sensor
Type: Unknown (65614)
Vendor: Samsung Inc.
Power: 0.75 mA
Max Range: 60000.0
Resolution: 1.0
What I learned is that the first listing is the standard light sensor, used to auto correct display brightness etc, reporting lux values. The one labeled CCT also detects the color components of the light. The third is a virtual sensor, where seamless indicates that this is used for battery saving. The android system polls the hardware sensor and updates thius seamless sensor when there are changes, and the user apps can poll this seemless sensor without using additional battery to access the hardware sensor directly.
In my case the first to apear in my sensor list is the one reporting the lux values, and the sensorInfo information in my script breaks after the first listing. On other hardware the list may be sorted different, so this may need to be considered in the hollywood script.
Other sensor info that might be useful. I just paste it here for reference.
Most used sensors for possible usage in hollywood:
Code: Select all
; Common sensor types
#SENSOR_ACCELEROMETER = 1
#SENSOR_MAGNETIC_FIELD = 2
#SENSOR_GYROSCOPE = 4
#SENSOR_LIGHT = 5
#SENSOR_PRESSURE = 6
#SENSOR_PROXIMITY = 8
#SENSOR_GRAVITY = 9
#SENSOR_LINEAR_ACCELERATION = 10
#SENSOR_ROTATION_VECTOR = 11
#SENSOR_HUMIDITY = 12
#SENSOR_AMBIENT_TEMPERATURE = 13
#SENSOR_STEP_COUNTER = 19Code: Select all
// Motion Sensors (most common)
Sensor.TYPE_ACCELEROMETER = 1 // Hardware accelerometer
Sensor.TYPE_MAGNETIC_FIELD = 2 // Magnetometer (compass)
Sensor.TYPE_ORIENTATION = 3 // DEPRECATED - Use rotation vector instead
Sensor.TYPE_GYROSCOPE = 4 // Gyroscope
Sensor.TYPE_LIGHT = 5 // Light sensor
Sensor.TYPE_PRESSURE = 6 // Barometer
Sensor.TYPE_TEMPERATURE = 7 // DEPRECATED - Split into ambient and device temperature
Sensor.TYPE_PROXIMITY = 8 // Proximity sensor
Sensor.TYPE_GRAVITY = 9 // Software gravity (derived from accelerometer)
Sensor.TYPE_LINEAR_ACCELERATION = 10 // Software linear accel (accel minus gravity)
Sensor.TYPE_ROTATION_VECTOR = 11 // Software orientation (sensor fusion)
Sensor.TYPE_RELATIVE_HUMIDITY = 12 // Humidity sensor
Sensor.TYPE_AMBIENT_TEMPERATURE = 13 // Ambient/room temperature
Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14 // Raw magnetometer data
Sensor.TYPE_GAME_ROTATION_VECTOR = 15 // Rotation without geomagnetic field
Sensor.TYPE_GYROSCOPE_UNCALIBRATED = 16 // Raw gyroscope data
Sensor.TYPE_SIGNIFICANT_MOTION = 17 // Significant motion detector
Sensor.TYPE_STEP_DETECTOR = 18 // Step detection
Sensor.TYPE_STEP_COUNTER = 19 // Step counting
Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20 // Rotation using geomagnetic field
Sensor.TYPE_HEART_RATE = 21 // Heart rate monitor
Sensor.TYPE_POSE_6DOF = 28 // 6 degrees of freedom pose
Sensor.TYPE_STATIONARY_DETECT = 29 // Stationary detection
Sensor.TYPE_MOTION_DETECT = 30 // Motion detection
Sensor.TYPE_HEART_BEAT = 31 // Heart beat detector
Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT = 34 // Low latency off-body detection
Sensor.TYPE_ACCELEROMETER_UNCALIBRATED = 35 // Raw accelerometer data-
xabierpayet
- Posts: 280
- Joined: Fri Feb 24, 2012 9:34 am
Re: Scoped storage methods for Android 10+
amazing, i must try this tonight, it´s superb
-
xabierpayet
- Posts: 280
- Joined: Fri Feb 24, 2012 9:34 am
Re: Scoped storage methods for Android 10+
i have it compiled and when i launch the program in android i have the next error:
java method hasfileaccess not found,current line 7 etc...
Any idea to fix it?
java method hasfileaccess not found,current line 7 etc...
Any idea to fix it?
-
xabierpayet
- Posts: 280
- Joined: Fri Feb 24, 2012 9:34 am
Re: Scoped storage methods for Android 10+
well, i have this working, i remove this part of code;
/*
ok = CallJavaMethod("hasAllFilesAccess", {ReturnType=#BOOLEAN})
If Not ok
NPrint("App needs ALL FILES ACCESS. Opening settings...")
CallJavaMethod("requestAllFilesAccess")
NPrint("Press to continue after granting permission")
WaitLeftMouse
EndIf
ok = CallJavaMethod("createFolder", {ReturnType=#BOOLEAN}, #STRING, "SensorList"); creates a new folder under /Downloads (Or eg. Documents if specified in java file)
*/
and all is working like a charm, the light sensor is very accurate here, i will change some things in the program, but this work very fine, thanks amyren
/*
ok = CallJavaMethod("hasAllFilesAccess", {ReturnType=#BOOLEAN})
If Not ok
NPrint("App needs ALL FILES ACCESS. Opening settings...")
CallJavaMethod("requestAllFilesAccess")
NPrint("Press to continue after granting permission")
WaitLeftMouse
EndIf
ok = CallJavaMethod("createFolder", {ReturnType=#BOOLEAN}, #STRING, "SensorList"); creates a new folder under /Downloads (Or eg. Documents if specified in java file)
*/
and all is working like a charm, the light sensor is very accurate here, i will change some things in the program, but this work very fine, thanks amyren
Re: Scoped storage methods for Android 10+
I notice in yout last post you made a workaround by skipping a few commands.xabierpayet wrote: ↑Sun Dec 07, 2025 8:51 pm i have it compiled and when i launch the program in android i have the next error:
java method hasfileaccess not found,current line 7 etc...
Any idea to fix it?
The hasAllFilesAccess is only needed when you want to store files externally.
In this case it uses it to save the sensor list as a textfile sensors.txt and not needed for using the lightsensor so you are good without it.
I am not sure why you got that error. The java command should exist in that java file I provided.
As mentioned in a comment in the java file, hasAllFilesAccess is only needed on android 10 or above. I dont know it it fails on older android versions as I only tested this on my own phone with android 16.
Does it write the sensor.txt file at all now, or did you alse remove the part where it write to the file?
-
xabierpayet
- Posts: 280
- Joined: Fri Feb 24, 2012 9:34 am
Re: Scoped storage methods for Android 10+
i have the sensor.txt file in android, is writed here android 13