Skip to content

Commit

Permalink
Optimize, reduce apk size
Browse files Browse the repository at this point in the history
  • Loading branch information
ThinhVu committed Mar 30, 2022
1 parent 17c9e09 commit 5e69e9b
Show file tree
Hide file tree
Showing 15 changed files with 163 additions and 158 deletions.
17 changes: 9 additions & 8 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,16 @@ android {
}

buildTypes {
debug {
postprocessing {
removeUnusedCode true
obfuscate false
optimizeCode true
}
}
release {
minifyEnabled false
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
Expand All @@ -26,12 +34,5 @@ android {
}

dependencies {
implementation 'org.slf4j:slf4j-simple:1.7.24'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.github.ThinhVu:javaext:1.0.4'
implementation("com.squareup.okhttp3:okhttp:4.9.3")
compile 'com.koushikdutta.ion:ion:3.1.0'
}
3 changes: 1 addition & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Yeeeeet">
android:supportsRtl="false">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
92 changes: 92 additions & 0 deletions app/src/main/java/com/sfbl/yeeeeet/Fetch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.sfbl.yeeeeet;

import android.os.Handler;
import android.os.Looper;

import com.sfbl.javaext.delegate.Action;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;

public final class Fetch {
private static final int TIMEOUT_MS = 1000000;

private static void getRequest(String url, Action._1<HttpURLConnection> cb) throws IOException {
final HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(TIMEOUT_MS);
connection.setReadTimeout(TIMEOUT_MS);
try {
connection.connect();
cb.$(connection);
} finally {
connection.disconnect();
}
}

public static void download(String url, String toFile) throws IOException {
File f = new File(toFile);
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
getRequest(url, conn -> {
try {
switch (conn.getResponseCode()) {
case 200:
case 201:
InputStream is = conn.getInputStream();
byte[] buffer = new byte[1024];
int byteRead = 0;
while ((byteRead = is.read(buffer)) > 0) {
fo.write(buffer, 0, byteRead);
}
is.close();
fo.flush();
fo.close();
}
} catch (Exception e) {
e.printStackTrace();
}
});
}

public static void getAsync(final String url, final Action._1<String> onFetch) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
AtomicReference<String> responseData = new AtomicReference<>();
try {
getRequest(url, conn -> {
try {
switch (conn.getResponseCode()) {
case 200:
case 201:
final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
final StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null)
builder.append(line).append("\n");
reader.close();
responseData.set(builder.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (IOException e) {
e.printStackTrace();
}

String finalResponseData = responseData.get();
handler.post(() -> onFetch.$(finalResponseData));
});
}
}
173 changes: 59 additions & 114 deletions app/src/main/java/com/sfbl/yeeeeet/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,19 @@
package com.sfbl.yeeeeet;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.StrictMode;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.koushikdutta.async.http.AsyncHttpClient;
import com.koushikdutta.async.http.AsyncHttpGet;
import com.koushikdutta.async.http.AsyncHttpResponse;
import com.koushikdutta.ion.Ion;
import com.sfbl.javaext.asyncAwait.Async;
import com.sfbl.javaext.utils.Strings;

import java.io.File;
import java.util.concurrent.ExecutionException;

public class MainActivity extends AppCompatActivity {
public class MainActivity extends Activity {
private final String TAG = "Main";

String proxyServer;
Expand All @@ -45,9 +34,10 @@ protected void onCreate(Bundle savedInstanceState) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

Ion.getDefault(this).getConscryptMiddleware().enable(false);
// Ion.getDefault(this).getConscryptMiddleware().enable(false);

SharedPreferences sharedPreferences = this.getSharedPreferences("storage", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
proxyServer = sharedPreferences.getString("proxy", "https://media.outstagram.xyz");
storePath = sharedPreferences.getString("store-path", "/storage/emulated/0/Pictures/Instagram/");

Expand All @@ -57,9 +47,7 @@ protected void onCreate(Bundle savedInstanceState) {
btnSetProxyServer = findViewById(R.id.btnSetProxy);
btnSetProxyServer.setOnClickListener(v -> {
proxyServer = edtProxyServer.getText().toString();
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("proxy", proxyServer);
editor.commit();
editor.apply();
});

Expand All @@ -69,9 +57,7 @@ protected void onCreate(Bundle savedInstanceState) {
btnStorePath = findViewById(R.id.btnSetStorePath);
btnStorePath.setOnClickListener(v -> {
storePath = edtStorePath.getText().toString();
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("store-path", storePath);
editor.commit();
editor.apply();
});

Expand All @@ -92,125 +78,84 @@ private void handleSendText(Intent intent) {
// https://www.instagram.com/p/CbSrMPmp_lQ/?utm_medium=share_sheet
String shareUrl = intent.getStringExtra(Intent.EXTRA_TEXT);
String mediaUrl = proxyServer + "/v1?url=" + shareUrl.replace("/?utm_medium=share_sheet", "") + "?__a=1";
parseContent(loadMetadata(mediaUrl));
Fetch.getAsync(mediaUrl, this::parseContent);
} catch (Exception e) {
Log.e(TAG, "handleSendText: ", e);
e.printStackTrace();
}
}

private void parseContent(String content) {
if (null == content || Strings.eq(content, "{}")) {
runOnUiThread(() -> {
Toast
.makeText(this.getApplicationContext(), "Failed to load metadata!", Toast.LENGTH_LONG)
.show();
});
this.finishAndRemoveTask();
}

JsonObject obj = new Gson().fromJson(content, JsonObject.class);

try {
String status = obj.get("status").getAsString();
if (status.equals("fail")) {
runOnUiThread(() -> Toast.makeText(this.getApplicationContext(), "Proxy server has been blocked by Instagram. Try another server!", Toast.LENGTH_LONG).show());
return;
if (null == content || content.equals("{}")) {
runOnUiThread(() -> {
Toast
.makeText(this.getApplicationContext(), "Failed to load metadata!", Toast.LENGTH_LONG)
.show();
});
this.finishAndRemoveTask();
}
} catch (Exception ignored) {}

JsonArray images = obj.get("items").getAsJsonArray();
for (int i = 0; i < images.size(); ++i) {
JsonObject item = images.get(i).getAsJsonObject();
int carousel_media_count = 0;
JSONObject obj = new JSONObject(content);

try {
carousel_media_count = item.get("carousel_media_count").getAsInt();
} catch (Exception ignored) {
}
if (carousel_media_count > 0) {
JsonArray carousel_media = item.getAsJsonArray("carousel_media");
for (int x = 0; x < carousel_media.size(); ++x) {
downloadMedia(carousel_media.get(x).getAsJsonObject());
String status = obj.getString("status");
if (status.equals("fail")) {
runOnUiThread(() -> Toast.makeText(this.getApplicationContext(), "Proxy server has been blocked by Instagram. Try another server!", Toast.LENGTH_LONG).show());
return;
}
} catch (Exception ignored) {}

JSONArray images = obj.getJSONArray("items");
for (int i = 0; i < images.length(); ++i) {
JSONObject item = images.getJSONObject(i);
int carousel_media_count = 0;
try {
carousel_media_count = item.getInt("carousel_media_count");
} catch (Exception ignored) {
}
if (carousel_media_count > 0) {
JSONArray carousel_media = item.getJSONArray("carousel_media");
for (int x = 0; x < carousel_media.length(); ++x) {
downloadMedia(carousel_media.getJSONObject(x));
}
} else {
downloadMedia(item);
}
} else {
downloadMedia(item);
this.finishAndRemoveTask();
}
this.finishAndRemoveTask();
} catch (Exception e) {
e.printStackTrace();
}
}

private void downloadMedia(JsonObject item) {
private void downloadMedia(JSONObject item) throws JSONException {

String id = item.get("id").getAsString();
String id = item.getString("id");
try {
// prefer video version than image
JsonArray video_versions = item.getAsJsonArray("video_versions");
if (video_versions != null && video_versions.size() > 0) {
JsonObject video_version = video_versions.get(0).getAsJsonObject();
String videoUrl = video_version.get("url").getAsString();
String saveTo = storePath + id + ".mp4";
downloadMediaFile(videoUrl, saveTo);
JSONArray video_versions = item.getJSONArray("video_versions");
if (video_versions != null && video_versions.length() > 0) {
JSONObject video_version = video_versions.getJSONObject(0);
Fetch.download(video_version.getString("url"), storePath + id + ".mp4");
return;
}
} catch (Exception e) {
Log.e(TAG, "downloadMedia: ", e);
}

} catch (Exception ignored) {}

JsonObject image_versions2 = item.getAsJsonObject("image_versions2");
int original_width = item.get("original_width").getAsInt();
int original_height = item.get("original_height").getAsInt();
JsonArray candidates = image_versions2.getAsJsonArray("candidates");

JSONObject image_versions2 = item.getJSONObject("image_versions2");
int original_width = item.getInt("original_width");
int original_height = item.getInt("original_height");
JSONArray candidates = image_versions2.getJSONArray("candidates");

for (int x = 0; x < candidates.size(); ++x) {
JsonObject candidate = candidates.get(x).getAsJsonObject();
if (candidate.get("width").getAsInt() == original_width && candidate.get("height").getAsInt() == original_height) {
String url = candidate.get("url").getAsString();
String saveTo = storePath + id + ".jpg";
for (int x = 0; x < candidates.length(); ++x) {
JSONObject candidate = candidates.getJSONObject(x);
if (candidate.getInt("width") == original_width && candidate.getInt("height") == original_height) {
try {
downloadMediaFile(url, saveTo);
Fetch.download(candidate.getString("url"), storePath + id + ".jpg");
} catch (Exception e) {
Log.e(TAG, "parseContent: ", e);
e.printStackTrace();
}
return;
}
}
}

private String loadMetadata(String metadataUrl) throws ExecutionException, InterruptedException {
Log.d(TAG, "loadMetadata");
return Async.await(resolve -> {
AsyncHttpGet get = new AsyncHttpGet(metadataUrl);
AsyncHttpClient.getDefaultInstance().executeString(get, new AsyncHttpClient.StringCallback() {
@Override
public void onCompleted(Exception e, AsyncHttpResponse source, String result) {
if (e != null) {
resolve.$(null);
} else {
resolve.$(result);
}
}
});
}
);
}

private void downloadMediaFile(String imageUrl, String saveTo) {
try {
Log.d(TAG, "downloadMediaFile: " + imageUrl + " then save to " + saveTo);
Ion.with(getApplicationContext())
.load(imageUrl)
.setTimeout(1000000)
.write(new File(saveTo))
.setCallback((e, file) -> {
if (e != null) {
Log.e(TAG, "DOWNLOAD_FILE:", e);
} else {
Log.d(TAG, "DOWNLOAD_FILE: Completed");
}
});
} catch (Exception e) {
Log.e(TAG, "downloadMediaFile: ", e);
}
}
}
4 changes: 2 additions & 2 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
Expand Down Expand Up @@ -56,4 +56,4 @@
android:text="Use must create the folder manually!!!" />

</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
Binary file removed app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary file not shown.
Binary file removed app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Binary file not shown.
Binary file removed app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary file not shown.
Binary file removed app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Binary file not shown.
Binary file removed app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary file not shown.
Binary file removed app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Binary file not shown.
Binary file removed app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary file not shown.
Binary file removed app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Binary file not shown.
Loading

0 comments on commit 5e69e9b

Please sign in to comment.