├── .gitignore
├── Info.plist
├── Makefile
├── README.md
├── ShortLook-API.h
├── WeChatContactPhotoProvider.h
├── WeChatContactPhotoProvider.m
└── control
/.gitignore:
--------------------------------------------------------------------------------
1 | # gitignore for Theos
2 |
3 | *.deb
4 | _/
5 | theos/
6 | theos
7 | .theos
8 | packages/
9 | ./packages/
10 | obj/
11 | ./obj/
12 | obj
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDisplayName
6 | WeChat
7 | DDNotificationExternalProviderClasses
8 |
9 | WeChatContactPhotoProvider
10 | com.tencent.xin
11 |
12 | DDNotificationExternalProviderAPIVersion
13 | 1
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | PACKAGE_VERSION = 1.0.2
3 | DEBUG = 0
4 | ARCHS = armv7 arm64
5 |
6 | include $(THEOS)/makefiles/common.mk
7 |
8 | BUNDLE_NAME = ShortLook-WeChat
9 | $(BUNDLE_NAME)_CFLAGS = -fobjc-arc
10 | $(BUNDLE_NAME)_FILES = $(wildcard *.m)
11 | $(BUNDLE_NAME)_FRAMEWORKS = UIKit MobileCoreServices
12 | $(BUNDLE_NAME)_INSTALL_PATH = /Library/Dynastic/ShortLook/Plugins/ContactPhotoProviders
13 |
14 | include $(THEOS_MAKE_PATH)/bundle.mk
15 |
16 | BUNDLE_PATH = $($(BUNDLE_NAME)_INSTALL_PATH)/$(BUNDLE_NAME).bundle
17 |
18 | internal-stage::
19 | $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)$(BUNDLE_PATH)$(ECHO_END)
20 | $(ECHO_NOTHING)cp Info.plist $(THEOS_STAGING_DIR)$(BUNDLE_PATH)/Info.plist$(ECHO_END)
21 |
22 | after-install::
23 | install.exec "killall -9 SpringBoard"
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ShortLook-WeChat
2 |
--------------------------------------------------------------------------------
/ShortLook-API.h:
--------------------------------------------------------------------------------
1 | //
2 | // WARNING:
3 | // This file contains the required headers to compile a tweak that
4 | // interacts with ShortLook's contact photo API. Do not modify the
5 | // headers within, or it could lead to unexpected behaviour.
6 | //
7 | // ---
8 | //
9 | // ShortLook-API.h
10 | //
11 | // Created by AppleBetas on 2018-05-18.
12 | // Copyright © 2018 Dynastic Development. All rights reserved.
13 | //
14 |
15 | #import
16 | @class NCNotificationRequest, UNNotificationContent;
17 |
18 | /// A ShortLook-displayable notification representing a real user notification sent by an application to the system.
19 | @interface DDUserNotification : NSObject
20 | /// A custom notification title, separate from the application's title.
21 | @property (nonatomic, retain, readonly) NSString *notificationTitle;
22 |
23 | /// A dictionary of any extra information included by ShortLook.
24 | @property (nonatomic, retain) NSDictionary *userInfo;
25 |
26 | /// The system notification request that created this notification.
27 | @property (nonatomic, readonly, retain) NCNotificationRequest *request;
28 |
29 | /// The user notification's content, sent by the application.
30 | - (UNNotificationContent *)content;
31 |
32 | /// A dictionary of any extra information provided by the application sending the notification.
33 | - (NSDictionary *)applicationUserInfo;
34 |
35 | /// The bundle identifier of the application this notification represents.
36 | - (NSString *)senderIdentifier;
37 | @end
38 |
39 | /// An object representing settings for the photo to be provided by a promise.
40 | @interface DDNotificationContactPhotoSettings: NSObject
41 | /// The background colour to show for the contact photo view if the provided image contains any transparency.
42 | @property (nonatomic, retain) UIColor *backgroundColor;
43 |
44 | /// Whether or not ShortLook should automatically cache the returned image from your provider and use it for all future notifications with the same photo identifier and application.
45 | @property (nonatomic, assign) BOOL usesCaching;
46 | @end
47 |
48 | /// A promise representing a commitment to providing a contact icon for a notification.
49 | @interface DDNotificationContactPhotoPromise: NSObject
50 | /// An object holding the settings pertaining to the photo to be displayed.
51 | @property (nonatomic, retain) DDNotificationContactPhotoSettings *settings;
52 |
53 | /// Whether or not this promise has already been resolved or rejected.
54 | @property (nonatomic, readonly, assign) BOOL isComplete;
55 |
56 | // MARK: - Provider methods
57 |
58 | /// Resolve this promise with the provided image, notifying ShortLook that you have received your image.
59 | /// - NOTE: This method should only be ran from within `addResolver:`.
60 | - (void)resolveWithImage:(UIImage *)image;
61 |
62 | /// Reject this promise, notifying ShortLook that you failed to receive an image.
63 | /// - NOTE: This method should only be ran from within `addResolver:`.
64 | - (void)reject;
65 | @end
66 |
67 | /// An offer to fulfill a promise representing a commitment to providing a contact icon for a notification.
68 | @interface DDNotificationContactPhotoPromiseOffer: NSObject
69 | /// A unique identifier for the photo that will be provided by this promise.
70 | @property (nonatomic, readonly, retain) NSString *photoIdentifier;
71 |
72 | /// A string to replace the notification's title with, if it is required for your provider's context. Set to "" to remove the provided notification's title.
73 | @property (nonatomic, retain) NSString *titleOverride;
74 |
75 | /// A string to replace the notification's subtitle with, if it is required for your provider's context. Set to "" to remove the provided notification's subtitle.
76 | @property (nonatomic, retain) NSString *subtitleOverride;
77 |
78 | /// A string to replace the notification's body with, if it is required for your provider's context. Set to "" to remove the provided notification's body.
79 | @property (nonatomic, retain) NSString *bodyOverride;
80 |
81 | /// Initialize a promise with the provided photo identifier.
82 | - (instancetype)initWithPhotoIdentifier:(NSString *)photoIdentifier;
83 |
84 | /// Create a promise offer that will return the image at the provided URL, if needed.
85 | + (instancetype)offerDownloadingPromiseWithPhotoIdentifier:(NSString *)photoIdentifier fromURL:(NSURL *)url;
86 |
87 | /// Create a promise offer that will return the image at the provided URL, if needed, with custom settings.
88 | + (instancetype)offerDownloadingPromiseWithPhotoIdentifier:(NSString *)photoIdentifier fromURL:(NSURL *)url withSettings:(DDNotificationContactPhotoSettings *)settings;
89 |
90 | /// Create a promise offer that will instantly return the provided image.
91 | + (instancetype)offerInstantlyResolvingPromiseWithPhotoIdentifier:(NSString *)photoIdentifier image:(UIImage *)image;
92 |
93 | /// Create a promise offer that will instantly return the provided image with custom settings.
94 | + (instancetype)offerInstantlyResolvingPromiseWithPhotoIdentifier:(NSString *)photoIdentifier image:(UIImage *)image withSettings:(DDNotificationContactPhotoSettings *)settings;
95 |
96 | /// Add the block that will run if your image is needed (as it will not be in some cases, such as if your image is already cached by ShortLook).
97 | /// If your provider does any long-running or asynchronous operations, they should be done using this method.
98 | /// Any code run inside the provider block will be performed on a background thread.
99 | - (void)fulfillWithBlock:(void (^)(DDNotificationContactPhotoPromise *promise))block;
100 | @end
101 |
102 | /// An object that can provide contact photos for ShortLook notifications.
103 | @protocol DDNotificationContactPhotoProviding
104 | @required
105 | /// Returns an offer to fulfill a promise to provide a contact photo for a notification.
106 | - (DDNotificationContactPhotoPromiseOffer *)contactPhotoPromiseOfferForNotification:(DDUserNotification *)notification;
107 | @end
108 |
--------------------------------------------------------------------------------
/WeChatContactPhotoProvider.h:
--------------------------------------------------------------------------------
1 | #import "ShortLook-API.h"
2 | #import
3 |
4 | @interface WeChatContactPhotoProvider : NSObject {
5 | sqlite3 *db;
6 | }
7 | - (DDNotificationContactPhotoPromiseOffer *)contactPhotoPromiseOfferForNotification:(DDUserNotification *)notification;
8 | @end
9 |
--------------------------------------------------------------------------------
/WeChatContactPhotoProvider.m:
--------------------------------------------------------------------------------
1 | #import "WeChatContactPhotoProvider.h"
2 | #import
3 |
4 | @interface FBApplicationInfo
5 | -(NSURL *)dataContainerURL;
6 | @end
7 |
8 | @interface LSApplicationProxy
9 | +(id)applicationProxyForIdentifier:(id)arg1;
10 | @end
11 |
12 | @implementation WeChatContactPhotoProvider
13 |
14 | - (DDNotificationContactPhotoPromiseOffer *)contactPhotoPromiseOfferForNotification:(DDUserNotification *)notification {
15 | FBApplicationInfo *appinfo = [LSApplicationProxy applicationProxyForIdentifier:@"com.tencent.xin"];
16 | NSString *appPath = [[appinfo dataContainerURL] path];
17 | NSString *contactID = notification.applicationUserInfo[@"u"];
18 | if (!contactID) return nil;
19 |
20 | NSString *loginInfoPath = [NSString stringWithFormat:@"%@/Documents/LocalInfo.lst", appPath];
21 | NSDictionary *loginInfo = [NSDictionary dictionaryWithContentsOfFile:loginInfoPath];
22 | NSString *userID = loginInfo[@"$objects"][2];
23 | NSString *userDataName = [self md5:userID];
24 | NSString *userContactsDBPath = [NSString stringWithFormat:@"%@/Documents/%@/DB/WCDB_Contact.sqlite", appPath, userDataName];
25 |
26 | if (sqlite3_open([userContactsDBPath UTF8String], &db) != SQLITE_OK) {
27 | sqlite3_close(db);
28 | return nil;
29 | }
30 |
31 | NSString *profileURLStr;
32 |
33 | NSString *sqlQuery = [NSString stringWithFormat:@"SELECT dbContactHeadImage FROM Friend WHERE userName = '%@'", contactID];
34 | sqlite3_stmt * statement;
35 |
36 | if (sqlite3_prepare_v2(db, [sqlQuery UTF8String], -1, &statement, nil) == SQLITE_OK) {
37 | while (sqlite3_step(statement) == SQLITE_ROW) {
38 | const void *head = sqlite3_column_blob(statement, 0);
39 | int size = sqlite3_column_bytes(statement, 0);
40 | NSData *data = [[NSData alloc] initWithBytes:head length:size];
41 | profileURLStr = [self getAvatarURLInData:data];
42 | }
43 | }
44 | sqlite3_close(db);
45 |
46 | NSRange range = [profileURLStr rangeOfString:@"/132" options:NSBackwardsSearch];
47 | if (range.location != NSNotFound) {
48 | //替换为高清头像
49 | profileURLStr = [profileURLStr stringByReplacingCharactersInRange:range withString:@"/0"];
50 | }
51 |
52 | if (!profileURLStr) return nil;
53 | NSURL *profileURL = [NSURL URLWithString:profileURLStr];
54 | if (!profileURL) return nil;
55 |
56 | return [NSClassFromString(@"DDNotificationContactPhotoPromiseOffer") offerDownloadingPromiseWithPhotoIdentifier:profileURLStr fromURL:profileURL];
57 | }
58 |
59 | - (NSString *)getAvatarURLInData:(NSData *)data{
60 | if (!data || data.length <= 8) {
61 | return @"";
62 | }
63 |
64 | int begin = 0;
65 | int end = 0;
66 |
67 | Byte *byteData = (Byte *)[data bytes];
68 | for(int i=0;i<[data length];i++){
69 | if (byteData[i] == 104 && begin == 0) {
70 | begin = i;
71 | }
72 |
73 | if (byteData[i] == 26 && end == 0) {
74 | end = i;
75 | }
76 | }
77 |
78 | if (begin > 0 && end > 0) {
79 | int len = end - begin;
80 | NSData* tempData = [data subdataWithRange:NSMakeRange(begin, len)];
81 | NSString* str = [[NSString alloc]initWithData:tempData encoding:NSASCIIStringEncoding];
82 |
83 | return str;
84 | }
85 |
86 | return @"";
87 | }
88 |
89 | - (NSString *)md5:(NSString *)input {
90 |
91 | const char *cStr = [input UTF8String];
92 |
93 | unsigned char digest[CC_MD5_DIGEST_LENGTH];
94 |
95 | CC_MD5( cStr, strlen(cStr), digest ); // This is the md5 call
96 |
97 | NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
98 |
99 | for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
100 | [output appendFormat:@"%02x", digest[i]];
101 | }
102 | return output;
103 | }
104 |
105 | @end
106 |
--------------------------------------------------------------------------------
/control:
--------------------------------------------------------------------------------
1 | Package: com.jerrytian.shortlook.plugin.contact-photo.wechat
2 | Name: WeChat Profile Pictures for ShortLook
3 | Depends: mobilesubstrate, co.dynastic.ios.tweak.shortlook
4 | Version: 1.0.2
5 | Architecture: iphoneos-arm
6 | Description: Show WeChat users' profile pictures in ShortLook when you receive a WeChat notification!
7 | Maintainer: Jianan Tian
8 | Author: Jianan Tian
9 | Section: Tweaks (ShortLook)
10 |
--------------------------------------------------------------------------------