This repository has been archived by the owner on Apr 18, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Note. Not working in this state. Portrait/Landscape transition must be done first.
- Loading branch information
0 parents
commit b690524
Showing
19 changed files
with
567 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,10 @@ | |||
*.iml | |||
*.iws | |||
*.ipr | |||
.idea/ | |||
.gradle/ | |||
local.properties | |||
|
|||
*/build/ | |||
|
|||
*.apk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,19 @@ | |||
buildscript { | |||
repositories { | |||
mavenCentral() | |||
} | |||
dependencies { | |||
classpath 'com.android.tools.build:gradle:0.4' | |||
} | |||
} | |||
apply plugin: 'android' | |||
|
|||
android { | |||
compileSdkVersion 'rovo89:Xposed Bridge API:17' | |||
buildToolsVersion "17.0.0" | |||
|
|||
defaultConfig { | |||
minSdkVersion 14 | |||
targetSdkVersion 17 | |||
} | |||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,24 @@ | |||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||
package="net.cantab.stammler.snapshare" | |||
android:versionCode="1" | |||
android:versionName="1.0"> | |||
|
|||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" /> | |||
|
|||
<application android:allowBackup="true" | |||
android:label="@string/app_name" | |||
android:icon="@drawable/ic_launcher"> | |||
<meta-data android:value="true" android:name="xposedmodule"/> | |||
<meta-data android:value="2.0*" android:name="xposedminversion"/> | |||
<meta-data android:value="Lets you share arbitrary Images via Snapchat." android:name="xposeddescription"/> | |||
<activity android:name=".ReceiveImageActivity" | |||
android:theme="@android:style/Theme.NoDisplay"> | |||
<intent-filter> | |||
<action android:name="android.intent.action.SEND" /> | |||
<category android:name="android.intent.category.DEFAULT" /> | |||
<data android:mimeType="image/*" /> | |||
</intent-filter> | |||
</activity> | |||
</application> | |||
|
|||
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1 @@ | |||
net.cantab.stammler.snapshare.Snapshare |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
134 changes: 134 additions & 0 deletions
134
Snapshare/src/main/java/net/cantab/stammler/snapshare/ReceiveImageActivity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,134 @@ | |||
package net.cantab.stammler.snapshare; | |||
|
|||
/** | |||
* Created by sebastian on 6/26/13. | |||
* | |||
*/ | |||
|
|||
import android.app.Activity; | |||
import android.content.ComponentName; | |||
import android.content.Intent; | |||
import android.graphics.Bitmap; | |||
import android.graphics.Matrix; | |||
import android.net.Uri; | |||
import android.os.Bundle; | |||
import android.provider.MediaStore; | |||
import android.util.DisplayMetrics; | |||
import android.util.Log; | |||
|
|||
import java.io.ByteArrayOutputStream; | |||
import java.io.FileNotFoundException; | |||
import java.io.IOException; | |||
|
|||
import static android.graphics.Bitmap.createBitmap; | |||
|
|||
import static net.cantab.stammler.snapshare.Snapshare.LOG_TAG; | |||
|
|||
public class ReceiveImageActivity extends Activity { | |||
private static final int JPEG_QUALITY = 85; | |||
|
|||
@Override | |||
public void onCreate (Bundle savedInstanceState) { | |||
super.onCreate(savedInstanceState); | |||
// Get intent, action and MIME type | |||
Intent intent = getIntent(); | |||
String action = intent.getAction(); | |||
String type = intent.getType(); | |||
Uri imageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM); | |||
if (Intent.ACTION_SEND.equals(action) && type.startsWith("image/")) { | |||
Log.d(LOG_TAG, "Received Image share of type " + type | |||
+ "\nand URI " + imageUri.toString() | |||
+ "\nCalling hooked Snapchat with same Intent."); | |||
|
|||
//InputStream iStream; | |||
ByteArrayOutputStream oStream = new ByteArrayOutputStream(); | |||
try { | |||
/*iStream = getContentResolver().openInputStream(imageUri); | |||
oStream = new ByteArrayOutputStream(iStream.available()); | |||
Log.d("Snapshare", "iStream.available(): " + iStream.available()); | |||
int byteSize = IOUtils.copy(iStream, oStream); | |||
Log.v("Snapshare", "Image size: " + byteSize/1024 + " kB");*/ | |||
/*TODO use BitmapFactory with inSampleSize magic to avoid using too much memory, | |||
see http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap */ | |||
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri); | |||
int width = bitmap.getWidth(); | |||
int height = bitmap.getHeight(); | |||
Log.d(LOG_TAG, "Original image w x h: " + width + " x " + height); | |||
// Portrait images have to be rotated 90 degrees clockwise for Snapchat to be displayed correctly | |||
if (height > width) { | |||
Log.d(LOG_TAG, "Portrait image detected, rotating 90 degrees clockwise."); | |||
Matrix matrix = new Matrix(); | |||
matrix.setRotate(270); | |||
bitmap = createBitmap(bitmap, 0, 0, width, height, matrix, true); | |||
// resetting width and height | |||
width = bitmap.getWidth(); | |||
height = bitmap.getHeight(); | |||
} | |||
|
|||
/* Scaling and cropping mayhem | |||
Snapchat will break if the image sent does not fit into the | |||
DisplayMetrics.widthPixels x DisplayMetrics.heightPixels rectangle (Display rectangle) | |||
and it will scale the image up if the Display rectangle is larger than the image, | |||
ignoring the image's ratio. | |||
So, we sample the image down such that the Display rectangle fits into it and touches one side. | |||
Then we crop the picture to that rectangle */ | |||
DisplayMetrics dm = new DisplayMetrics(); | |||
getWindowManager().getDefaultDisplay().getMetrics(dm); | |||
int dWidth = dm.widthPixels; | |||
int dHeight = dm.heightPixels; | |||
Log.d(LOG_TAG, "Display metrics w x h: " + dWidth + " x " + dHeight); | |||
// DisplayMetrics' values depend on the phone's tilt, so we normalize them to landscape mode | |||
if (dHeight > dWidth) { | |||
Log.d(LOG_TAG, "Normalizing display metrics to landscape mode."); | |||
int temp = dWidth; | |||
dWidth = dHeight; | |||
dHeight = temp; | |||
} | |||
/* If the image properly covers the Display rectangle, we mark it as a "large" image | |||
and are going to scale it down. We make this distinction because we don't wanna | |||
scale the image up if it is smaller than the Display rectangle. */ | |||
boolean largeImage = ((width > dWidth) & (height > dHeight)); | |||
Log.d(LOG_TAG, "Large image? " + largeImage); | |||
int imageToDisplayRatio = width * dHeight - height * dWidth; | |||
if (imageToDisplayRatio > 0) { | |||
// i.e., width/height > dWidth/dHeight, so have to crop from left and right: | |||
int newWidth = (dWidth * height / dHeight); | |||
Log.d(LOG_TAG, "New width after cropping left & right: " + newWidth); | |||
bitmap = createBitmap(bitmap, (width - newWidth) / 2, 0, newWidth, height); | |||
|
|||
} else if (imageToDisplayRatio < 0) { | |||
// i.e., width/height < dWidth/dHeight, so have to crop from top and bottom: | |||
int newHeight = (dHeight * width / dWidth); | |||
Log.d(LOG_TAG, "New height after cropping top & bottom: " + newHeight); | |||
bitmap = createBitmap(bitmap, 0, (height - newHeight) / 2, width, newHeight); | |||
} | |||
if (largeImage) { | |||
Log.d(LOG_TAG, "Scaling down."); | |||
bitmap = Bitmap.createScaledBitmap(bitmap, dWidth, dHeight, true); | |||
} | |||
/// Scaling and cropping finished, ready to pass to Snapchat | |||
|
|||
bitmap.compress(Bitmap.CompressFormat.JPEG, JPEG_QUALITY, oStream); | |||
Log.d(LOG_TAG, "outputStream size: " + oStream.size()/1024 + " kB = " + oStream.size() + " B"); | |||
byte [] imageData = oStream.toByteArray(); | |||
int dataLength = imageData.length; | |||
Log.d(LOG_TAG, "byte array size: " + dataLength/1024 + " kB = " + dataLength + " B"); | |||
bitmap.recycle(); | |||
intent.putExtra("imageData", imageData); | |||
intent.putExtra("dataLength", dataLength); | |||
intent.setComponent(ComponentName.unflattenFromString("com.snapchat.android/.LandingPageActivity")); | |||
startActivity(intent); | |||
} catch (FileNotFoundException e) { | |||
Log.e(LOG_TAG, "File not found!", e); | |||
} catch (IOException e) { | |||
Log.e(LOG_TAG, "IO Error!", e); | |||
} | |||
} | |||
//call finish at the end to close the wrapper | |||
finish(); | |||
} | |||
} | |||
//TODO video sharing: | |||
// intent.putExtra("videoFile", imageUri.toString()); LATER for video share. should be a file:// string, pointing to a .mp4 file |
94 changes: 94 additions & 0 deletions
94
Snapshare/src/main/java/net/cantab/stammler/snapshare/Snapshare.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,94 @@ | |||
package net.cantab.stammler.snapshare; | |||
|
|||
import android.app.Activity; | |||
import android.content.Intent; | |||
import android.graphics.Bitmap; | |||
import android.graphics.BitmapFactory; | |||
import android.os.Bundle; | |||
import android.util.DisplayMetrics; | |||
import android.util.Log; | |||
import android.view.LayoutInflater; | |||
import android.view.ViewGroup; | |||
|
|||
import java.lang.reflect.InvocationTargetException; | |||
|
|||
import de.robv.android.xposed.IXposedHookLoadPackage; | |||
import de.robv.android.xposed.XC_MethodHook; | |||
import de.robv.android.xposed.XposedBridge; | |||
import de.robv.android.xposed.callbacks.XC_LoadPackage; | |||
|
|||
import static de.robv.android.xposed.XposedHelpers.callMethod; | |||
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; | |||
import static de.robv.android.xposed.XposedHelpers.getObjectField; | |||
import static de.robv.android.xposed.XposedHelpers.newInstance; | |||
|
|||
/** | |||
* Created by sebastian on 6/26/13. | |||
* | |||
*/ | |||
|
|||
public class Snapshare implements IXposedHookLoadPackage { | |||
public static final String LOG_TAG = "Snapshare"; | |||
|
|||
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { | |||
if (!lpparam.packageName.equals("com.snapchat.android")) | |||
return; | |||
else | |||
XposedBridge.log("Snapshare: Snapchat load detected."); | |||
|
|||
findAndHookMethod("com.snapchat.android.LandingPageActivity", lpparam.classLoader, "onCreate", Bundle.class, new XC_MethodHook() { | |||
@Override | |||
protected void afterHookedMethod(MethodHookParam param) throws Throwable { | |||
Object thiz = param.thisObject; | |||
//Log.i(LOG_TAG, "Invoking super.onCreate()"); | |||
//Activity.class.getMethod("onCreate", Bundle.class).invoke(thiz, param.args[0]); | |||
Intent intent = (Intent) callSuperMethod(thiz, "getIntent"); | |||
String type = intent.getType(); | |||
Log.d(LOG_TAG, "intent type: " + type); | |||
//Log.i(LOG_TAG, "param.args[0] toString():" + ((Bundle) param.args[0]).toString()); | |||
/*if (type != null) { | |||
if (type.startsWith("image/")) { | |||
intent.setComponent(ComponentName.unflattenFromString("com.snapchat.android/.SnapPreviewActivity")); | |||
//callSuperMethod(thiz, "startActivity", intent); | |||
thiz.getClass().getMethod("startActivity", Intent.class).invoke(thiz, intent); | |||
callSuperMethod(thiz, "finish"); | |||
} | |||
}*/ | |||
int dataLength = intent.getIntExtra("dataLength", -1); | |||
Log.d(LOG_TAG, "data length: " + dataLength); | |||
if (dataLength > 0) { | |||
Bitmap imageBitmap = BitmapFactory.decodeByteArray(intent.getByteArrayExtra("imageData"), 0, dataLength); | |||
Class SnapCapturedEventClass = Class.forName("com.snapchat.android.util.eventbus.SnapCapturedEvent", true, thiz.getClass().getClassLoader()); | |||
Object captureEvent = newInstance(SnapCapturedEventClass, imageBitmap); | |||
callMethod(thiz, "onSnapCaptured", captureEvent); | |||
} | |||
//callSuperMethod(thiz, "finish"); | |||
} | |||
}); | |||
findAndHookMethod("com.snapchat.android.SnapPreviewFragment", lpparam.classLoader, "onCreateView", LayoutInflater.class, ViewGroup.class, Bundle.class, new XC_MethodHook() { | |||
@Override | |||
protected void afterHookedMethod(MethodHookParam param) throws Throwable { | |||
Object thiz = param.thisObject; | |||
Activity curActivity = (Activity) callSuperMethod(thiz, "getActivity"); | |||
Log.d(LOG_TAG, "Fr> Current Activity: " + curActivity.getLocalClassName()); | |||
Intent intent = curActivity.getIntent(); | |||
Log.d(LOG_TAG, "Fr> Intent type: " + intent.getType()); | |||
Bitmap bitmap = (Bitmap) getObjectField(thiz, "mImageBitmap"); | |||
Log.d(LOG_TAG, "Fr> Width: " + bitmap.getWidth() + ", Height: " + bitmap.getHeight()); | |||
DisplayMetrics dm = (DisplayMetrics) getObjectField(thiz, "mDisplayMetrics"); | |||
Log.d(LOG_TAG, "Fr> SnapPreviewActivity Display Metrics w x h: " + dm.widthPixels + " x " + dm.heightPixels); | |||
} | |||
}); | |||
} | |||
/* | |||
* callSuperMethod() | |||
* | |||
* XposedHelpers.callMethod() cannot call methods of the super class of an object, because it | |||
* uses getDeclaredMethods(). So we have to implement this little helper, which should work | |||
* similar to callMethod(). Furthermore, the exceptions from getMethod() are passed on. | |||
* See http://forum.xda-developers.com/showpost.php?p=42598280&postcount=1753 | |||
*/ | |||
private Object callSuperMethod(Object obj, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { | |||
return obj.getClass().getMethod(methodName).invoke(obj); | |||
} | |||
} |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,3 @@ | |||
<resources> | |||
<string name="app_name">Snapshare</string> | |||
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,20 @@ | |||
<resources> | |||
|
|||
<!-- | |||
Base application theme, dependent on API level. This theme is replaced | |||
by AppBaseTheme from res/values-vXX/styles.xml on newer devices. | |||
--> | |||
<style name="AppBaseTheme" parent="android:Theme"> | |||
<!-- | |||
Theme customizations available in newer API levels can go in | |||
res/values-vXX/styles.xml, while customizations related to | |||
backward-compatibility can go here. | |||
--> | |||
</style> | |||
|
|||
<!-- Application theme. --> | |||
<style name="AppTheme" parent="AppBaseTheme"> | |||
<!-- All customizations that are NOT specific to a particular API-level can go here. --> | |||
</style> | |||
|
|||
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1 @@ | |||
// Top-level build file where you can add configuration options common to all sub-projects/modules. |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,6 @@ | |||
#Wed Apr 10 15:27:10 PDT 2013 | |||
distributionBase=GRADLE_USER_HOME | |||
distributionPath=wrapper/dists | |||
zipStoreBase=GRADLE_USER_HOME | |||
zipStorePath=wrapper/dists | |||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip |
Oops, something went wrong.