前言

Leko 今天 (2020-08-26) 在 Twitter 上看到一篇值得分享的好消息,想分享到 FB 上

剛好想起之前人家介紹的 Tuigram 還有 Twiger 可以把 tweet 變成圖片

用了才發現,兩個軟體居然都只支援分享到 Instagram 上 (Leko 不是很喜歡 IG :(

所以就想說改一下 APP 讓他們支援分享到任意 App 上 yay

值得分享的好消息

會用到的東西

  • APK 檔
  • apktool
  • JADX
  • Android SDK

 

本文

Tuigram (入門級)

我們的目標很簡單,把原本的分享到 Instagram 的按鈕改成可以隨意分享

 

1. 反編譯

> apktool.bat d '.\Tuigram Share tweets on instagram_v1.1.7_apkpure.com.apk' -r

在原地會看到一個新生出來的資料夾

 

2. 看 Code

用 JADX 開啟 APK

按放大鏡把 Text search 叫出來 (順便等他 Decompile 完)

想著總之先搜尋 instagram 吧,沒想到一搜就中,yay

 

要代換的程式碼

看一下官方 Doc,可以看到我們需要的 Code

1
2
3
4
5
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uriToImage);
shareIntent.setType("image/jpeg");
startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to)));

JADX 搜尋出來的 Code 如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public boolean b(String str, String str2) {
Intent intent = new Intent("com.instagram.share.ADD_TO_STORY");
Uri a2 = FileProvider.a(this, "com.vcoud.fileprovider", new File(str2));
intent.setType(str);
intent.putExtra("interactive_asset_uri", a2);
grantUriPermission("com.instagram.android", a2, 1);
if (getPackageManager().resolveActivity(intent, 0) == null) {
return false;
}
startActivityForResult(intent, 0);
return true;
}

// 中間省略

public void a(String str, String str2) {
Intent intent = new Intent("android.intent.action.SEND");
intent.setType(str);
Uri a2 = FileProvider.a(this, "com.vcoud.fileprovider", new File(str2));
intent.addFlags(1);
intent.putExtra("android.intent.extra.STREAM", a2);
intent.setComponent(new ComponentName("com.instagram.android", "com.instagram.share.handleractivity.ShareHandlerActivity"));
startActivity(intent);
}

可以明顯的看出 b 是分享到 IG 限動,a 是普通的 IG 貼文

a 跟我們的目標最像,所以我們改 a (差別只在 a:7setComponent 還有範例 Code 的 Intent.createChooser )

 

3. 改 Code

我們打開步驟 1 生出的資料夾,在 smali/com/vcoud/tuigram/MainActivity.smali 找到我們的 target

首先我們要先把 setComponent 刪掉 (很顯然的就是 .line 11 的部分,全部註解掉)

接著加上 Intent.createChooser 的部分

1
2
3
4
5
6
startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to)));

// 事實上相當於
String title = getResources().getText(R.string.send_to);
Intent chooser = Intent.createChooser(shareIntent, title);
startActivity(chooser)

所以我們寫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# String title = "分享至"
# 這個是上面的 getResources().getText(R.string.send_to)
# 會作為分享畫面的標題
const-string v1, "分享至"

# 在 .line 5 可以看到 new-instance v0, Landroid/content/Intent;
# 也就是 Intent
# v1 則是我們的 string
# 接受一個 Intent 和一個 CharSequence 作為參數,回傳一個 Intent
# 其中 createChooser 是一個 static method
invoke-static {v0, v1}, Landroid/content/Intent;->createChooser(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;

# 把上一步驟的結果 (chooser) 移到 v1 裡 (v1 是不會再用到的 string 所以不用擔心)
move-result-object v1

# 接著我們 startActivity
# 注意到 class method 的第一個參數是 class instance,類似 python 裡的 Class function 第一個參數總是 self
# 原本他 call 的是 {p0, v0},其中 v0 是原本的 Intent
# 現在我們把 v0 換成我們剛剛新建立的 chooser,也就是 v1
invoke-virtual {p0, v1}, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V

結果長得像這樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    # ...

.line 11
# new-instance p1, Landroid/content/ComponentName;

# const-string p2, "com.instagram.android"

# const-string v1, "com.instagram.share.handleractivity.ShareHandlerActivity"

# invoke-direct {p1, p2, v1}, Landroid/content/ComponentName;-><init>(Ljava/lang/String;Ljava/lang/String;)V

# invoke-virtual {v0, p1}, Landroid/content/Intent;->setComponent(Landroid/content/ComponentName;)Landroid/content/Intent;


.line 12
const-string v1, "分享至"

invoke-static {v0, v1}, Landroid/content/Intent;->createChooser(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;

move-result-object v1

invoke-virtual {p0, v1}, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V

return-void
.end method

# ...

儲存。

 

4. 打包回去

> apktool.bat b '.\Tuigram Share tweets on instagram_v1.1.7_apkpure.com\'

結果會在 .\Tuigram Share tweets on instagram_v1.1.7_apkpure.com\dist\ 裡面找到
build 好的 APK,簽名後裝進手機裡就大功告成辣 owo

 

4.1 簽名

首先建立 Keystore

> keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

keytool 在 JDK 裡面有附

 

接著簽名

> %ANDROID_SDK%\build-tools\%VERSION%\apksigner.bat sign --ks %KEYSTORE% %APK_PATH%

%ANDROID_SDK%, %VERSION%, %KEYSTORE%, %APK_PATH% 換掉就可以簽名了

 

yay