How to take screenshots on Android

Godot Version

4.4.1

Question

Hello! I’ve been having issues with the latest installment, and have been looking for accurate searches with no good results. How do you make a screenshot .png of .jpg in your Android? I was able to only find code for uploading to desktop, and one outdated source with no replacement.
Where do I begin? I’m just trying to access screenshots like any other app but can’t.

If anyone is curious here is a computer snip of my game

If anyone could tell me how to use Android shots and not computer snips that would be nice.

You can get a image from a viewport and save that to create a “screenshot”

var view_image: Image = get_viewport().get_texture().get_image()
view_image.save_png("user://path_to_screenshot.png")

It won’t do it trying multiple ways like /storage/emulated/0 or just user:// here’s my code.

func _save_image():
var view_image: Image = get_viewport().get_texture().get_image()
view_image.save_png(“user://Pictures/screenshot.png”)

You will also need android permissions to write to “external storage” i.e. the phone’s documents

This topic recently covered some of that, hard to follow because of the huge code pastes and little else information. But they claim to get this path to work: "/storage/emulated/0/Pictures/Drawing/" + filename

This is indeed hard to follow. I was able to even find “write external” in project settings. I don’t know where to begin with this example. I even added OS.request_permissions. Look

func _save_image():
OS.request_permissions()
var file_name = “screenshot.png”
var view_image: Image = get_viewport().get_texture().get_image()
view_image.save_png(“user://storage/emulated/0/Pictures/”+file_name)

Does anyone have anything better? Last time I checked, you couldn’t even make screenshots a long time ago.

So I put it in. No dice. It’s outdated at viewport to begin with. Look along with my regular code

extends CanvasLayer

func _request_storage_permissions():
if OS.get_name() == “Android”:
# In Godot 4.3, permissions are handled differently
# You need to declare them in the export template or project settings
OS.request_permissions()

func _export_drawing_to_png():
var image = get_viewport().get_texture().get_image()
image.flip_y()

# Build the save path - Use user:// for Android compatibility
var base_dir: String
if OS.get_name() == "Android":
	# On Android, use the app's internal storage or documents directory
	base_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
	if base_dir.is_empty():
		# Fallback to user:// directory (app's internal storage)
		base_dir = OS.get_user_data_dir()
else:
	# On other platforms, use the Pictures directory
	base_dir = OS.get_system_dir(OS.SYSTEM_DIR_PICTURES)

var drawing_dir := base_dir.path_join("Drawing")

# Ensure the "Drawing" folder exists
if not DirAccess.dir_exists_absolute(drawing_dir):
	var dir_result = DirAccess.make_dir_recursive_absolute(drawing_dir)
	if dir_result != OK:
		print("❌ Failed to create directory:", drawing_dir, " Error:", dir_result)
		return

# Create a filename with a valid timestamp
var timestamp := Time.get_datetime_string_from_system().replace(":", "-").replace(" ", "_")
var filename := "screenshot" + timestamp + ".png"
var file_path := drawing_dir.path_join(filename)

print("📁 Attempting to save to:", file_path)

# Save the image as PNG
# On Android, also try to copy to a more accessible location
if OS.get_name() == "Android":
	var accessible_path = "/storage/emulated/0/Pictures/Drawing/" + filename
	image.save_png(accessible_path)

func _physics_process(delta):
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
#print(global.color)
if global.color == 0:
if Input.is_action_just_pressed(“ui_input_4”):
global.color = 4
$Pixel/Active.play(“Smile”)
elif Input.is_action_just_pressed(“ui_input_5”):
global.color = 5
$Pixel/Active.play(“Egg”)
elif Input.is_action_just_pressed(“ui_input_6”):
global.color = 6
$Pixel/Active.play(“Puss”)
elif Input.is_action_just_pressed(“ui_input_7”):
global.color = 7
$Pixel/Active.play(“Statue”)
elif Input.is_action_just_pressed(“ui_input_8”):
global.color = 8
$Pixel/Active.play(“Pupupuputy”)
elif Input.is_action_just_pressed(“ui_input_9”):
global.color = 9
$Pixel/Active.play(“Black”)
elif Input.is_action_just_pressed(“ui_input_10”):
global.color = 10
$Pixel/Active.play(“Red”)
elif Input.is_action_just_pressed(“ui_input_11”):
global.color = 11
$Pixel/Active.play(“Yellow”)
elif Input.is_action_just_pressed(“ui_input_12”):
global.color = 12
$Pixel/Active.play(“Blue”)
elif Input.is_action_just_pressed(“ui_input_13”):
global.color = 13
$Pixel/Active.play(“Green”)
elif Input.is_action_just_pressed(“ui_input_14”):
global.color = 14
$Pixel/Active.play(“Orange”)
elif Input.is_action_just_pressed(“ui_input_15”):
global.color = 15
$Pixel/Active.play(“Purple”)
elif Input.is_action_just_pressed(“ui_input_16”):
global.color = 16
$Pixel/Active.play(“Pink”)
elif Input.is_action_just_pressed(“ui_input_17”):
global.color = 17
$Pixel/Active.play(“Turquoise”)
elif Input.is_action_just_pressed(“ui_input_18”):
global.color = 18
$Pixel/Active.play(“Brown”)
elif Input.is_action_just_pressed(“ui_input_19”):
global.color = 19
$Pixel/Active.play(“Erase”)
elif Input.is_action_just_pressed(“ui_input_20”):
global.color = 20
$Pixel/Active.play(“Camera”)
elif global.color != 0:
if Input.is_action_just_pressed(“ui_input_4”):
global.color = 4
$Pixel/Active.play(“Smile”)
elif Input.is_action_just_pressed(“ui_input_5”):
global.color = 5
$Pixel/Active.play(“Egg”)
elif Input.is_action_just_pressed(“ui_input_6”):
global.color = 6
$Pixel/Active.play(“Puss”)
elif Input.is_action_just_pressed(“ui_input_7”):
global.color = 7
$Pixel/Active.play(“Statue”)
elif Input.is_action_just_pressed(“ui_input_8”):
global.color = 8
$Pixel/Active.play(“Pupupuputy”)
elif Input.is_action_just_pressed(“ui_input_9”):
global.color = 9
$Pixel/Active.play(“Black”)
elif Input.is_action_just_pressed(“ui_input_10”):
global.color = 10
$Pixel/Active.play(“Red”)
elif Input.is_action_just_pressed(“ui_input_11”):
global.color = 11
$Pixel/Active.play(“Yellow”)
elif Input.is_action_just_pressed(“ui_input_12”):
global.color = 12
$Pixel/Active.play(“Blue”)
elif Input.is_action_just_pressed(“ui_input_13”):
global.color = 13
$Pixel/Active.play(“Green”)
elif Input.is_action_just_pressed(“ui_input_14”):
global.color = 14
$Pixel/Active.play(“Orange”)
elif Input.is_action_just_pressed(“ui_input_15”):
global.color = 15
$Pixel/Active.play(“Purple”)
elif Input.is_action_just_pressed(“ui_input_16”):
global.color = 16
$Pixel/Active.play(“Pink”)
elif Input.is_action_just_pressed(“ui_input_17”):
global.color = 17
$Pixel/Active.play(“Turquoise”)
elif Input.is_action_just_pressed(“ui_input_18”):
global.color = 18
$Pixel/Active.play(“Brown”)
elif Input.is_action_just_pressed(“ui_input_19”):
global.color = 0
$Pixel/Active.play(“Empty”)
elif Input.is_action_just_pressed(“ui_input_20”):
$Pixel/Active.play(“CamPress”)
_export_drawing_to_png()
elif Input.is_action_just_released(“ui_input_20”):
global.color = 20
$Pixel/Active.play(“Camera”)

Alright, I don’t know if things like these have been implemented so I’m stuck.
I listen to this guy on path here’s this for simplicity

func _pic():
var view_image: Image = get_viewport().get_texture().get_image()
view_image.save_png(“/storage/emulated/0/Pictures/screenshots.png”)

If anyone could tell me a solution (Or not. May not be implemented)?

Hi!

I managed to take a screenshot of my drawings on Android. You’ll need to export the drawing in .png format from the viewport to the Documents folder and not to the Pictures folder, because Android doesn’t want to export files to the Pictures folder for security reasons, but also because on Android, the Documents folder isn’t locked, unlike the other folders.

I’ve tried all the other folders other than Pictures without success. Only the Documents folder works.

Also make sure you’re on a recent version of Android as there is no Documents folder on Android 10 and earlier.

Also, remember to check the Read External Storage, Write External Storage, and Manage External Storage permissions when exporting to Android.

Here’s an example of my script so you can get some inspiration.

func request_storage_permissions():
	if OS.get_name() == "Android":
		# In Godot 4.3, permissions are handled differently
		# You need to declare them in the export template or project settings
		OS.request_permissions()

func export_drawing_to_png():
	var tile_size = tilemap_layer.tile_set.tile_size
	var tilemap_size = (max_bounds - min_bounds + Vector2i.ONE) * tile_size
	
	# Create the viewport for capturing the TileMap rendering
	var viewport := SubViewport.new()
	viewport.size = tilemap_size
	viewport.render_target_clear_mode = SubViewport.CLEAR_MODE_ALWAYS
	viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
	viewport.transparent_bg = false
	viewport.disable_3d = true
	
	# Duplicate the TileMap into a temporary container
	var container := Node2D.new()
	var tilemap_copy := tilemap_layer.duplicate()
	tilemap_copy.position = -min_bounds * tile_size  # Center at the origin
	container.add_child(tilemap_copy)
	viewport.add_child(container)
	
	# Add the viewport to the scene so it gets rendered
	add_child(viewport)
	await RenderingServer.frame_post_draw  # Wait for the rendering to complete
	
	# Retrieve the image from the viewport texture
	var image = viewport.get_texture().get_image()
	
	# Clean up
	remove_child(viewport)
	viewport.queue_free()
	
	# Build the save path - Use user:// for Android compatibility
	var base_dir: String
	if OS.get_name() == "Android":
		# On Android, use the app's internal storage or documents directory
		base_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
		if base_dir.is_empty():
			# Fallback to user:// directory (app's internal storage)
			base_dir = OS.get_user_data_dir()
	else:
		# On other platforms, use the Pictures directory
		base_dir = OS.get_system_dir(OS.SYSTEM_DIR_PICTURES)
	
	var drawing_dir := base_dir.path_join("Drawing")
	
	# Ensure the "Drawing" folder exists
	if not DirAccess.dir_exists_absolute(drawing_dir):
		var dir_result = DirAccess.make_dir_recursive_absolute(drawing_dir)
		if dir_result != OK:
			print("❌ Failed to create directory:", drawing_dir, " Error:", dir_result)
			return
	
	# Create a filename with a valid timestamp
	var timestamp := Time.get_datetime_string_from_system().replace(":", "-").replace(" ", "_")
	var filename := "export_tilemap_" + timestamp + ".png"
	var file_path := drawing_dir.path_join(filename)
	
	print("📁 Attempting to save to:", file_path)
	
	# Save the image as PNG
	var err := image.save_png(file_path)
	if err == OK:
		print("✅ Export PNG succeeded:", file_path)
		# On Android, also try to copy to a more accessible location
		if OS.get_name() == "Android":
			var accessible_path = "/storage/emulated/0/Pictures/Drawing/" + filename
			var copy_err = image.save_png(accessible_path)
			if copy_err == OK:
				print("✅ Also saved to accessible location:", accessible_path)
			else:
				print("⚠️ Could not save to accessible location:", copy_err)
	else:
		print("❌ Error during PNG export:", err)
		print("📁 Tried path:", file_path)

I was ‘bout to say, “wait till till Godot creators noticed”, but… SLAM-SLAM-SLAM! I figured it out!
Look, and when they better implement it, get ta changin’

func request_storage_permissions():
if OS.get_name() == “Android”:
# In Godot 4.3, permissions are handled differently
# You need to declare them in the export template or project settings
OS.request_permissions()

func export_drawing_to_png():
var image: Image = get_viewport().get_texture().get_image()

# Build the save path - Use user:// for Android compatibility
var base_dir: String
if OS.get_name() == "Android":
	# On Android, use the app's internal storage or documents directory
	base_dir = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
	if base_dir.is_empty():
		# Fallback to user:// directory (app's internal storage)
		base_dir = OS.get_user_data_dir()
else:
	# On other platforms, use the Pictures directory
	base_dir = OS.get_system_dir(OS.SYSTEM_DIR_PICTURES)

var drawing_dir := base_dir.path_join("Drawing")

# Ensure the "Drawing" folder exists
if not DirAccess.dir_exists_absolute(drawing_dir):
	var dir_result = DirAccess.make_dir_recursive_absolute(drawing_dir)
	if dir_result != OK:
		print("❌ Failed to create directory:", drawing_dir, " Error:", dir_result)
		return

# Create a filename with a valid timestamp
var timestamp := Time.get_datetime_string_from_system().replace(":", "-").replace(" ", "_")
var filename := "export_tilemap_" + timestamp + ".png"
var file_path := drawing_dir.path_join(filename)

print("📁 Attempting to save to:", file_path)

# Save the image as PNG
var err := image.save_png(file_path)
if err == OK:
	print("✅ Export PNG succeeded:", file_path)
	# On Android, also try to copy to a more accessible location
	if OS.get_name() == "Android":
		var accessible_path = "/storage/emulated/0/Documents/Drawing/" + filename
		var copy_err = image.save_png(accessible_path)
		if copy_err == OK:
			print("✅ Also saved to accessible location:", accessible_path)
		else:
			print("⚠️ Could not save to accessible location:", copy_err)
else:
	print("❌ Error during PNG export:", err)
	print("📁 Tried path:", file_path)