Merge remote-tracking branch 'remotes/origin/trz/rename' into trz/rebase
This commit is contained in:
commit
4f0061293e
51 changed files with 424 additions and 2466 deletions
|
@ -1,7 +1,9 @@
|
|||
###Conversations+ ChangeLog
|
||||
|
||||
####Version 0.0.6
|
||||
* Fixes FS#95: NPE when opening message details for failed file transfer
|
||||
* Implements FS#89: Change about information
|
||||
* Implements FS#84: Setting for location to store received pictures
|
||||
* Implements FS#83: Reload from last received message
|
||||
* Fixes FS#82: Strange layout in share with activity
|
||||
* Fixes FS#81: Interactive message loading causes "jumps"
|
||||
|
@ -12,6 +14,7 @@
|
|||
* Fixes FS#47: Setting "WLAN only" no longer works for received links
|
||||
* Implements FS#26: Introduce dialog to choose whether to send resized picture or original picture
|
||||
* Implements FS#24: Introduce setting for picture resizing
|
||||
* Implements FS#19: Received and Sent pictures are automatically stored in public picture folder
|
||||
* Partially implements FS#6: Change "Report bug to developer" - Reporting conference changed to c+bugs@conference.thedevstack.de
|
||||
|
||||
####Version 0.0.5
|
||||
|
|
31
build.gradle
31
build.gradle
|
@ -20,26 +20,33 @@ allprojects {
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
flatDir {
|
||||
dirs 'libs/3rdParty', 'libs/3rdParty/zxing'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Local JAR files
|
||||
//compile fileTree(dir: 'libs/zxing', includes: ['core-3.1.0.jar', 'android-integration-3.1.0.jar'])
|
||||
compile name: 'core-3.1.0' //zxing
|
||||
compile name: 'android-integration-3.1.0' //zxing
|
||||
compile name: 'libidn-1.15'
|
||||
compile name: 'minidns-0.1.3'
|
||||
compile name: 'org.otr4j-0.22'
|
||||
compile name: 'bcprov-jdk15on-1.51'
|
||||
compile name: 'EnhancedListView-0.3.4', ext: 'aar'
|
||||
compile name: 'ShortcutBadger-1.1.1', ext: 'aar'
|
||||
compile name: 'swipy-1.2.1', ext: 'aar'
|
||||
|
||||
// Local modules
|
||||
compile project(':libs:openpgp-api-lib')
|
||||
compile project(':libs:MemorizingTrustManager')
|
||||
compile 'com.android.support:support-v13:21.0.3'
|
||||
compile 'org.bouncycastle:bcprov-jdk15on:1.51'
|
||||
compile 'org.jitsi:org.otr4j:0.22'
|
||||
compile 'org.gnu.inet:libidn:1.15'
|
||||
compile 'com.google.zxing:core:3.1.0'
|
||||
compile 'com.google.zxing:android-integration:3.1.0'
|
||||
compile 'de.measite.minidns:minidns:0.1.3'
|
||||
compile 'de.timroes.android:EnhancedListView:0.3.4'
|
||||
compile 'me.leolin:ShortcutBadger:1.1.1@aar'
|
||||
compile project(':libs:emojicon')
|
||||
compile project(':libs:colorpicker')
|
||||
compile project(':libs:SwipyRefreshLayout')
|
||||
compile project(':libs:thedevstacklogcat')
|
||||
|
||||
// Android dependencies
|
||||
compile 'com.android.support:support-v13:21.0.3'
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
BIN
libs/3rdParty/EnhancedListView-0.3.4.aar
vendored
Normal file
BIN
libs/3rdParty/EnhancedListView-0.3.4.aar
vendored
Normal file
Binary file not shown.
BIN
libs/3rdParty/ShortcutBadger-1.1.1.aar
vendored
Normal file
BIN
libs/3rdParty/ShortcutBadger-1.1.1.aar
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
libs/3rdParty/minidns-0.1.3.jar
vendored
Normal file
BIN
libs/3rdParty/minidns-0.1.3.jar
vendored
Normal file
Binary file not shown.
BIN
libs/3rdParty/org.otr4j-0.22.jar
vendored
Normal file
BIN
libs/3rdParty/org.otr4j-0.22.jar
vendored
Normal file
Binary file not shown.
BIN
libs/3rdParty/swipy-1.2.1.aar
vendored
Normal file
BIN
libs/3rdParty/swipy-1.2.1.aar
vendored
Normal file
Binary file not shown.
1
libs/SwipyRefreshLayout/.gitignore
vendored
1
libs/SwipyRefreshLayout/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
/build
|
|
@ -1,24 +0,0 @@
|
|||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 22
|
||||
versionCode 1
|
||||
versionName "1.2.1"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:22.2.1'
|
||||
}
|
17
libs/SwipyRefreshLayout/proguard-rules.pro
vendored
17
libs/SwipyRefreshLayout/proguard-rules.pro
vendored
|
@ -1,17 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/oliviergoutay/Documents/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
|
@ -1,8 +0,0 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.orangegangsters.github.swipyrefreshlayout.library">
|
||||
|
||||
<application android:allowBackup="true">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.orangegangsters.github.swipyrefreshlayout.library;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RadialGradient;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.shapes.OvalShape;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/**
|
||||
* Private class created to work around issues with AnimationListeners being
|
||||
* called before the animation is actually complete and support shadows on older
|
||||
* platforms.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
class CircleImageView extends ImageView {
|
||||
|
||||
private static final int KEY_SHADOW_COLOR = 0x1E000000;
|
||||
private static final int FILL_SHADOW_COLOR = 0x3D000000;
|
||||
// PX
|
||||
private static final float X_OFFSET = 0f;
|
||||
private static final float Y_OFFSET = 1.75f;
|
||||
private static final float SHADOW_RADIUS = 3.5f;
|
||||
private static final int SHADOW_ELEVATION = 4;
|
||||
|
||||
private Animation.AnimationListener mListener;
|
||||
private int mShadowRadius;
|
||||
|
||||
public CircleImageView(Context context, int color, final float radius) {
|
||||
super(context);
|
||||
final float density = getContext().getResources().getDisplayMetrics().density;
|
||||
final int diameter = (int) (radius * density * 2);
|
||||
final int shadowYOffset = (int) (density * Y_OFFSET);
|
||||
final int shadowXOffset = (int) (density * X_OFFSET);
|
||||
|
||||
mShadowRadius = (int) (density * SHADOW_RADIUS);
|
||||
|
||||
ShapeDrawable circle;
|
||||
if (elevationSupported()) {
|
||||
circle = new ShapeDrawable(new OvalShape());
|
||||
ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
|
||||
} else {
|
||||
OvalShape oval = new OvalShadow(mShadowRadius, diameter);
|
||||
circle = new ShapeDrawable(oval);
|
||||
ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, circle.getPaint());
|
||||
circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,
|
||||
KEY_SHADOW_COLOR);
|
||||
final int padding = (int) mShadowRadius;
|
||||
// set padding so the inner image sits correctly within the shadow.
|
||||
setPadding(padding, padding, padding, padding);
|
||||
}
|
||||
circle.getPaint().setColor(color);
|
||||
setBackgroundDrawable(circle);
|
||||
}
|
||||
|
||||
private boolean elevationSupported() {
|
||||
return android.os.Build.VERSION.SDK_INT >= 21;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
if (!elevationSupported()) {
|
||||
setMeasuredDimension(getMeasuredWidth() + mShadowRadius*2, getMeasuredHeight()
|
||||
+ mShadowRadius*2);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAnimationListener(Animation.AnimationListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart() {
|
||||
super.onAnimationStart();
|
||||
if (mListener != null) {
|
||||
mListener.onAnimationStart(getAnimation());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd() {
|
||||
super.onAnimationEnd();
|
||||
if (mListener != null) {
|
||||
mListener.onAnimationEnd(getAnimation());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the background color of the circle image view.
|
||||
*/
|
||||
public void setBackgroundColor(int colorRes) {
|
||||
if (getBackground() instanceof ShapeDrawable) {
|
||||
final Resources res = getResources();
|
||||
((ShapeDrawable) getBackground()).getPaint().setColor(res.getColor(colorRes));
|
||||
}
|
||||
}
|
||||
|
||||
private class OvalShadow extends OvalShape {
|
||||
private RadialGradient mRadialGradient;
|
||||
private int mShadowRadius;
|
||||
private Paint mShadowPaint;
|
||||
private int mCircleDiameter;
|
||||
|
||||
public OvalShadow(int shadowRadius, int circleDiameter) {
|
||||
super();
|
||||
mShadowPaint = new Paint();
|
||||
mShadowRadius = shadowRadius;
|
||||
mCircleDiameter = circleDiameter;
|
||||
mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2,
|
||||
mShadowRadius, new int[] {
|
||||
FILL_SHADOW_COLOR, Color.TRANSPARENT
|
||||
}, null, Shader.TileMode.CLAMP);
|
||||
mShadowPaint.setShader(mRadialGradient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas, Paint paint) {
|
||||
final int viewWidth = CircleImageView.this.getWidth();
|
||||
final int viewHeight = CircleImageView.this.getHeight();
|
||||
canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius),
|
||||
mShadowPaint);
|
||||
canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,722 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.orangegangsters.github.swipyrefreshlayout.library;
|
||||
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.Transformation;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Fancy progress indicator for Material theme.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
class MaterialProgressDrawable extends Drawable implements Animatable {
|
||||
private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
|
||||
private static final Interpolator END_CURVE_INTERPOLATOR = new EndCurveInterpolator();
|
||||
private static final Interpolator START_CURVE_INTERPOLATOR = new StartCurveInterpolator();
|
||||
private static final Interpolator EASE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@IntDef({LARGE, DEFAULT})
|
||||
public @interface ProgressDrawableSize {}
|
||||
// Maps to ProgressBar.Large style
|
||||
static final int LARGE = 0;
|
||||
// Maps to ProgressBar default style
|
||||
static final int DEFAULT = 1;
|
||||
|
||||
// Maps to ProgressBar default style
|
||||
private static final int CIRCLE_DIAMETER = 40;
|
||||
private static final float CENTER_RADIUS = 8.75f; //should add up to 10 when + stroke_width
|
||||
private static final float STROKE_WIDTH = 2.5f;
|
||||
|
||||
// Maps to ProgressBar.Large style
|
||||
private static final int CIRCLE_DIAMETER_LARGE = 56;
|
||||
private static final float CENTER_RADIUS_LARGE = 12.5f;
|
||||
private static final float STROKE_WIDTH_LARGE = 3f;
|
||||
|
||||
private final int[] COLORS = new int[] {
|
||||
Color.BLACK
|
||||
};
|
||||
|
||||
/** The duration of a single progress spin in milliseconds. */
|
||||
private static final int ANIMATION_DURATION = 1000 * 80 / 60;
|
||||
|
||||
/** The number of points in the progress "star". */
|
||||
private static final float NUM_POINTS = 5f;
|
||||
/** The list of animators operating on this drawable. */
|
||||
private final ArrayList<Animation> mAnimators = new ArrayList<Animation>();
|
||||
|
||||
/** The indicator ring, used to manage animation state. */
|
||||
private final Ring mRing;
|
||||
|
||||
/** Canvas rotation in degrees. */
|
||||
private float mRotation;
|
||||
|
||||
/** Layout info for the arrowhead in dp */
|
||||
private static final int ARROW_WIDTH = 10;
|
||||
private static final int ARROW_HEIGHT = 5;
|
||||
private static final float ARROW_OFFSET_ANGLE = 5;
|
||||
|
||||
/** Layout info for the arrowhead for the large spinner in dp */
|
||||
private static final int ARROW_WIDTH_LARGE = 12;
|
||||
private static final int ARROW_HEIGHT_LARGE = 6;
|
||||
private static final float MAX_PROGRESS_ARC = .8f;
|
||||
|
||||
private Resources mResources;
|
||||
private View mParent;
|
||||
private Animation mAnimation;
|
||||
private float mRotationCount;
|
||||
private double mWidth;
|
||||
private double mHeight;
|
||||
private Animation mFinishAnimation;
|
||||
|
||||
public MaterialProgressDrawable(Context context, View parent) {
|
||||
mParent = parent;
|
||||
mResources = context.getResources();
|
||||
|
||||
mRing = new Ring(mCallback);
|
||||
mRing.setColors(COLORS);
|
||||
|
||||
updateSizes(DEFAULT);
|
||||
setupAnimators();
|
||||
}
|
||||
|
||||
private void setSizeParameters(double progressCircleWidth, double progressCircleHeight,
|
||||
double centerRadius, double strokeWidth, float arrowWidth, float arrowHeight) {
|
||||
final Ring ring = mRing;
|
||||
final DisplayMetrics metrics = mResources.getDisplayMetrics();
|
||||
final float screenDensity = metrics.density;
|
||||
|
||||
mWidth = progressCircleWidth * screenDensity;
|
||||
mHeight = progressCircleHeight * screenDensity;
|
||||
ring.setStrokeWidth((float) strokeWidth * screenDensity);
|
||||
ring.setCenterRadius(centerRadius * screenDensity);
|
||||
ring.setColorIndex(0);
|
||||
ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity);
|
||||
ring.setInsets((int) mWidth, (int) mHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the overall size for the progress spinner. This updates the radius
|
||||
* and stroke width of the ring.
|
||||
*
|
||||
* @param size One of {@link com.orangegangsters.github.swiperefreshlayout.MaterialProgressDrawable.LARGE} or
|
||||
* {@link com.orangegangsters.github.swiperefreshlayout.MaterialProgressDrawable.DEFAULT}
|
||||
*/
|
||||
public void updateSizes(@ProgressDrawableSize int size) {
|
||||
if (size == LARGE) {
|
||||
setSizeParameters(CIRCLE_DIAMETER_LARGE, CIRCLE_DIAMETER_LARGE, CENTER_RADIUS_LARGE,
|
||||
STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE, ARROW_HEIGHT_LARGE);
|
||||
} else {
|
||||
setSizeParameters(CIRCLE_DIAMETER, CIRCLE_DIAMETER, CENTER_RADIUS, STROKE_WIDTH,
|
||||
ARROW_WIDTH, ARROW_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param show Set to true to display the arrowhead on the progress spinner.
|
||||
*/
|
||||
public void showArrow(boolean show) {
|
||||
mRing.setShowArrow(show);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scale Set the scale of the arrowhead for the spinner.
|
||||
*/
|
||||
public void setArrowScale(float scale) {
|
||||
mRing.setArrowScale(scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start and end trim for the progress spinner arc.
|
||||
*
|
||||
* @param startAngle start angle
|
||||
* @param endAngle end angle
|
||||
*/
|
||||
public void setStartEndTrim(float startAngle, float endAngle) {
|
||||
mRing.setStartTrim(startAngle);
|
||||
mRing.setEndTrim(endAngle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount of rotation to apply to the progress spinner.
|
||||
*
|
||||
* @param rotation Rotation is from [0..1]
|
||||
*/
|
||||
public void setProgressRotation(float rotation) {
|
||||
mRing.setRotation(rotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the background color of the circle image view.
|
||||
*/
|
||||
public void setBackgroundColor(int color) {
|
||||
mRing.setBackgroundColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the colors used in the progress animation from color resources.
|
||||
* The first color will also be the color of the bar that grows in response
|
||||
* to a user swipe gesture.
|
||||
*
|
||||
* @param colors
|
||||
*/
|
||||
public void setColorSchemeColors(int... colors) {
|
||||
mRing.setColors(colors);
|
||||
mRing.setColorIndex(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
return (int) mHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
return (int) mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas c) {
|
||||
final Rect bounds = getBounds();
|
||||
final int saveCount = c.save();
|
||||
c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
|
||||
mRing.draw(c, bounds);
|
||||
c.restoreToCount(saveCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mRing.setAlpha(alpha);
|
||||
}
|
||||
|
||||
public int getAlpha() {
|
||||
return mRing.getAlpha();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
mRing.setColorFilter(colorFilter);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
void setRotation(float rotation) {
|
||||
mRotation = rotation;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private float getRotation() {
|
||||
return mRotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
final ArrayList<Animation> animators = mAnimators;
|
||||
final int N = animators.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final Animation animator = animators.get(i);
|
||||
if (animator.hasStarted() && !animator.hasEnded()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
mAnimation.reset();
|
||||
mRing.storeOriginals();
|
||||
// Already showing some part of the ring
|
||||
if (mRing.getEndTrim() != mRing.getStartTrim()) {
|
||||
mParent.startAnimation(mFinishAnimation);
|
||||
} else {
|
||||
mRing.setColorIndex(0);
|
||||
mRing.resetOriginals();
|
||||
mParent.startAnimation(mAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
mParent.clearAnimation();
|
||||
setRotation(0);
|
||||
mRing.setShowArrow(false);
|
||||
mRing.setColorIndex(0);
|
||||
mRing.resetOriginals();
|
||||
}
|
||||
|
||||
private void setupAnimators() {
|
||||
final Ring ring = mRing;
|
||||
final Animation finishRingAnimation = new Animation() {
|
||||
public void applyTransformation(float interpolatedTime, Transformation t) {
|
||||
// shrink back down and complete a full rotation before starting other circles
|
||||
// Rotation goes between [0..1].
|
||||
float targetRotation = (float) (Math.floor(ring.getStartingRotation()
|
||||
/ MAX_PROGRESS_ARC) + 1f);
|
||||
final float startTrim = ring.getStartingStartTrim()
|
||||
+ (ring.getStartingEndTrim() - ring.getStartingStartTrim())
|
||||
* interpolatedTime;
|
||||
ring.setStartTrim(startTrim);
|
||||
final float rotation = ring.getStartingRotation()
|
||||
+ ((targetRotation - ring.getStartingRotation()) * interpolatedTime);
|
||||
ring.setRotation(rotation);
|
||||
ring.setArrowScale(1 - interpolatedTime);
|
||||
}
|
||||
};
|
||||
finishRingAnimation.setInterpolator(EASE_INTERPOLATOR);
|
||||
finishRingAnimation.setDuration(ANIMATION_DURATION/2);
|
||||
finishRingAnimation.setAnimationListener(new Animation.AnimationListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
ring.goToNextColor();
|
||||
ring.storeOriginals();
|
||||
ring.setShowArrow(false);
|
||||
mParent.startAnimation(mAnimation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
});
|
||||
final Animation animation = new Animation() {
|
||||
@Override
|
||||
public void applyTransformation(float interpolatedTime, Transformation t) {
|
||||
// The minProgressArc is calculated from 0 to create an angle that
|
||||
// matches the stroke width.
|
||||
final float minProgressArc = (float) Math.toRadians(ring.getStrokeWidth()
|
||||
/ (2 * Math.PI * ring.getCenterRadius()));
|
||||
final float startingEndTrim = ring.getStartingEndTrim();
|
||||
final float startingTrim = ring.getStartingStartTrim();
|
||||
final float startingRotation = ring.getStartingRotation();
|
||||
|
||||
// Offset the minProgressArc to where the endTrim is located.
|
||||
final float minArc = MAX_PROGRESS_ARC - minProgressArc;
|
||||
final float endTrim = startingEndTrim
|
||||
+ (minArc * START_CURVE_INTERPOLATOR.getInterpolation(interpolatedTime));
|
||||
ring.setEndTrim(endTrim);
|
||||
|
||||
final float startTrim = startingTrim
|
||||
+ (MAX_PROGRESS_ARC * END_CURVE_INTERPOLATOR
|
||||
.getInterpolation(interpolatedTime));
|
||||
ring.setStartTrim(startTrim);
|
||||
|
||||
final float rotation = startingRotation + (0.25f * interpolatedTime);
|
||||
ring.setRotation(rotation);
|
||||
|
||||
float groupRotation = ((720.0f / NUM_POINTS) * interpolatedTime)
|
||||
+ (720.0f * (mRotationCount / NUM_POINTS));
|
||||
setRotation(groupRotation);
|
||||
}
|
||||
};
|
||||
animation.setRepeatCount(Animation.INFINITE);
|
||||
animation.setRepeatMode(Animation.RESTART);
|
||||
animation.setInterpolator(LINEAR_INTERPOLATOR);
|
||||
animation.setDuration(ANIMATION_DURATION);
|
||||
animation.setAnimationListener(new Animation.AnimationListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
mRotationCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
ring.storeOriginals();
|
||||
ring.goToNextColor();
|
||||
ring.setStartTrim(ring.getEndTrim());
|
||||
mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
|
||||
}
|
||||
});
|
||||
mFinishAnimation = finishRingAnimation;
|
||||
mAnimation = animation;
|
||||
}
|
||||
|
||||
private final Callback mCallback = new Callback() {
|
||||
@Override
|
||||
public void invalidateDrawable(Drawable d) {
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleDrawable(Drawable d, Runnable what, long when) {
|
||||
scheduleSelf(what, when);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unscheduleDrawable(Drawable d, Runnable what) {
|
||||
unscheduleSelf(what);
|
||||
}
|
||||
};
|
||||
|
||||
private static class Ring {
|
||||
private final RectF mTempBounds = new RectF();
|
||||
private final Paint mPaint = new Paint();
|
||||
private final Paint mArrowPaint = new Paint();
|
||||
|
||||
private final Callback mCallback;
|
||||
|
||||
private float mStartTrim = 0.0f;
|
||||
private float mEndTrim = 0.0f;
|
||||
private float mRotation = 0.0f;
|
||||
private float mStrokeWidth = 5.0f;
|
||||
private float mStrokeInset = 2.5f;
|
||||
|
||||
private int[] mColors;
|
||||
// mColorIndex represents the offset into the available mColors that the
|
||||
// progress circle should currently display. As the progress circle is
|
||||
// animating, the mColorIndex moves by one to the next available color.
|
||||
private int mColorIndex;
|
||||
private float mStartingStartTrim;
|
||||
private float mStartingEndTrim;
|
||||
private float mStartingRotation;
|
||||
private boolean mShowArrow;
|
||||
private Path mArrow;
|
||||
private float mArrowScale;
|
||||
private double mRingCenterRadius;
|
||||
private int mArrowWidth;
|
||||
private int mArrowHeight;
|
||||
private int mAlpha;
|
||||
private final Paint mCirclePaint = new Paint();
|
||||
private int mBackgroundColor;
|
||||
|
||||
public Ring(Callback callback) {
|
||||
mCallback = callback;
|
||||
|
||||
mPaint.setStrokeCap(Paint.Cap.SQUARE);
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setStyle(Style.STROKE);
|
||||
|
||||
mArrowPaint.setStyle(Style.FILL);
|
||||
mArrowPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
public void setBackgroundColor(int color) {
|
||||
mBackgroundColor = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dimensions of the arrowhead.
|
||||
*
|
||||
* @param width Width of the hypotenuse of the arrow head
|
||||
* @param height Height of the arrow point
|
||||
*/
|
||||
public void setArrowDimensions(float width, float height) {
|
||||
mArrowWidth = (int) width;
|
||||
mArrowHeight = (int) height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the progress spinner
|
||||
*/
|
||||
public void draw(Canvas c, Rect bounds) {
|
||||
final RectF arcBounds = mTempBounds;
|
||||
arcBounds.set(bounds);
|
||||
arcBounds.inset(mStrokeInset, mStrokeInset);
|
||||
|
||||
final float startAngle = (mStartTrim + mRotation) * 360;
|
||||
final float endAngle = (mEndTrim + mRotation) * 360;
|
||||
float sweepAngle = endAngle - startAngle;
|
||||
|
||||
mPaint.setColor(mColors[mColorIndex]);
|
||||
c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
|
||||
|
||||
drawTriangle(c, startAngle, sweepAngle, bounds);
|
||||
|
||||
if (mAlpha < 255) {
|
||||
mCirclePaint.setColor(mBackgroundColor);
|
||||
mCirclePaint.setAlpha(255 - mAlpha);
|
||||
c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2,
|
||||
mCirclePaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) {
|
||||
if (mShowArrow) {
|
||||
if (mArrow == null) {
|
||||
mArrow = new Path();
|
||||
mArrow.setFillType(Path.FillType.EVEN_ODD);
|
||||
} else {
|
||||
mArrow.reset();
|
||||
}
|
||||
|
||||
// Adjust the position of the triangle so that it is inset as
|
||||
// much as the arc, but also centered on the arc.
|
||||
float inset = (int) mStrokeInset / 2 * mArrowScale;
|
||||
float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX());
|
||||
float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY());
|
||||
|
||||
// Update the path each time. This works around an issue in SKIA
|
||||
// where concatenating a rotation matrix to a scale matrix
|
||||
// ignored a starting negative rotation. This appears to have
|
||||
// been fixed as of API 21.
|
||||
mArrow.moveTo(0, 0);
|
||||
mArrow.lineTo(mArrowWidth * mArrowScale, 0);
|
||||
mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight
|
||||
* mArrowScale));
|
||||
mArrow.offset(x - inset, y);
|
||||
mArrow.close();
|
||||
// draw a triangle
|
||||
mArrowPaint.setColor(mColors[mColorIndex]);
|
||||
c.rotate(startAngle + sweepAngle - ARROW_OFFSET_ANGLE, bounds.exactCenterX(),
|
||||
bounds.exactCenterY());
|
||||
c.drawPath(mArrow, mArrowPaint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the colors the progress spinner alternates between.
|
||||
*
|
||||
* @param colors Array of integers describing the colors. Must be non-<code>null</code>.
|
||||
*/
|
||||
public void setColors(@NonNull int[] colors) {
|
||||
mColors = colors;
|
||||
// if colors are reset, make sure to reset the color index as well
|
||||
setColorIndex(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index Index into the color array of the color to display in
|
||||
* the progress spinner.
|
||||
*/
|
||||
public void setColorIndex(int index) {
|
||||
mColorIndex = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proceed to the next available ring color. This will automatically
|
||||
* wrap back to the beginning of colors.
|
||||
*/
|
||||
public void goToNextColor() {
|
||||
mColorIndex = (mColorIndex + 1) % (mColors.length);
|
||||
}
|
||||
|
||||
public void setColorFilter(ColorFilter filter) {
|
||||
mPaint.setColorFilter(filter);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param alpha Set the alpha of the progress spinner and associated arrowhead.
|
||||
*/
|
||||
public void setAlpha(int alpha) {
|
||||
mAlpha = alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current alpha of the progress spinner and arrowhead.
|
||||
*/
|
||||
public int getAlpha() {
|
||||
return mAlpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param strokeWidth Set the stroke width of the progress spinner in pixels.
|
||||
*/
|
||||
public void setStrokeWidth(float strokeWidth) {
|
||||
mStrokeWidth = strokeWidth;
|
||||
mPaint.setStrokeWidth(strokeWidth);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public float getStrokeWidth() {
|
||||
return mStrokeWidth;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setStartTrim(float startTrim) {
|
||||
mStartTrim = startTrim;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public float getStartTrim() {
|
||||
return mStartTrim;
|
||||
}
|
||||
|
||||
public float getStartingStartTrim() {
|
||||
return mStartingStartTrim;
|
||||
}
|
||||
|
||||
public float getStartingEndTrim() {
|
||||
return mStartingEndTrim;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setEndTrim(float endTrim) {
|
||||
mEndTrim = endTrim;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public float getEndTrim() {
|
||||
return mEndTrim;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setRotation(float rotation) {
|
||||
mRotation = rotation;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public float getRotation() {
|
||||
return mRotation;
|
||||
}
|
||||
|
||||
public void setInsets(int width, int height) {
|
||||
final float minEdge = (float) Math.min(width, height);
|
||||
float insets;
|
||||
if (mRingCenterRadius <= 0 || minEdge < 0) {
|
||||
insets = (float) Math.ceil(mStrokeWidth / 2.0f);
|
||||
} else {
|
||||
insets = (float) (minEdge / 2.0f - mRingCenterRadius);
|
||||
}
|
||||
mStrokeInset = insets;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public float getInsets() {
|
||||
return mStrokeInset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param centerRadius Inner radius in px of the circle the progress
|
||||
* spinner arc traces.
|
||||
*/
|
||||
public void setCenterRadius(double centerRadius) {
|
||||
mRingCenterRadius = centerRadius;
|
||||
}
|
||||
|
||||
public double getCenterRadius() {
|
||||
return mRingCenterRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param show Set to true to show the arrow head on the progress spinner.
|
||||
*/
|
||||
public void setShowArrow(boolean show) {
|
||||
if (mShowArrow != show) {
|
||||
mShowArrow = show;
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scale Set the scale of the arrowhead for the spinner.
|
||||
*/
|
||||
public void setArrowScale(float scale) {
|
||||
if (scale != mArrowScale) {
|
||||
mArrowScale = scale;
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The amount the progress spinner is currently rotated, between [0..1].
|
||||
*/
|
||||
public float getStartingRotation() {
|
||||
return mStartingRotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the start / end trim are offset to begin with, store them so that
|
||||
* animation starts from that offset.
|
||||
*/
|
||||
public void storeOriginals() {
|
||||
mStartingStartTrim = mStartTrim;
|
||||
mStartingEndTrim = mEndTrim;
|
||||
mStartingRotation = mRotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the progress spinner to default rotation, start and end angles.
|
||||
*/
|
||||
public void resetOriginals() {
|
||||
mStartingStartTrim = 0;
|
||||
mStartingEndTrim = 0;
|
||||
mStartingRotation = 0;
|
||||
setStartTrim(0);
|
||||
setEndTrim(0);
|
||||
setRotation(0);
|
||||
}
|
||||
|
||||
private void invalidateSelf() {
|
||||
mCallback.invalidateDrawable(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Squishes the interpolation curve into the second half of the animation.
|
||||
*/
|
||||
private static class EndCurveInterpolator extends AccelerateDecelerateInterpolator {
|
||||
@Override
|
||||
public float getInterpolation(float input) {
|
||||
return super.getInterpolation(Math.max(0, (input - 0.5f) * 2.0f));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Squishes the interpolation curve into the first half of the animation.
|
||||
*/
|
||||
private static class StartCurveInterpolator extends AccelerateDecelerateInterpolator {
|
||||
@Override
|
||||
public float getInterpolation(float input) {
|
||||
return super.getInterpolation(Math.min(1, input * 2.0f));
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,27 +0,0 @@
|
|||
package com.orangegangsters.github.swipyrefreshlayout.library;
|
||||
|
||||
/**
|
||||
* Created by oliviergoutay on 1/23/15.
|
||||
*/
|
||||
public enum SwipyRefreshLayoutDirection {
|
||||
|
||||
TOP(0),
|
||||
BOTTOM(1),
|
||||
BOTH(2);
|
||||
|
||||
private int mValue;
|
||||
|
||||
SwipyRefreshLayoutDirection(int value) {
|
||||
this.mValue = value;
|
||||
}
|
||||
|
||||
public static SwipyRefreshLayoutDirection getFromInt(int value) {
|
||||
for (SwipyRefreshLayoutDirection direction : SwipyRefreshLayoutDirection.values()) {
|
||||
if (direction.mValue == value) {
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
return BOTH;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<declare-styleable name="SwipyRefreshLayout">
|
||||
<attr name="direction">
|
||||
<enum name="top" value="0" />
|
||||
<enum name="bottom" value="1" />
|
||||
<enum name="both" value="2" />
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
17
libs/colorpicker/proguard-rules.pro
vendored
17
libs/colorpicker/proguard-rules.pro
vendored
|
@ -1,17 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Developer/android-sdk-macosx/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
|
@ -12,7 +12,6 @@ android {
|
|||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1406,6 +1406,7 @@ public final class EmojiconHandler {
|
|||
}
|
||||
|
||||
public static final Map<Pattern, Integer> ANDROID_EMOTICONS = new HashMap<Pattern, Integer>();
|
||||
private static final Pattern TIME_AND_SCORING_PATTERN = Pattern.compile("[0-9]{1,3}:[0-9]{1,3}");
|
||||
|
||||
private static final Spannable.Factory spannableFactory = Spannable.Factory
|
||||
.getInstance();
|
||||
|
@ -1448,9 +1449,14 @@ public final class EmojiconHandler {
|
|||
boolean hasChanges = false;
|
||||
|
||||
Matcher webUrlMatcher = Patterns.WEB_URL.matcher(spannable);
|
||||
Set<Pair<Integer, Integer>> webUrls = new HashSet<Pair<Integer, Integer>>();
|
||||
Set<Pair<Integer, Integer>> falsePositives = new HashSet<Pair<Integer, Integer>>();
|
||||
while (webUrlMatcher.find()) {
|
||||
webUrls.add(Pair.create(webUrlMatcher.start(), webUrlMatcher.end()));
|
||||
falsePositives.add(Pair.create(webUrlMatcher.start(), webUrlMatcher.end()));
|
||||
}
|
||||
|
||||
Matcher timeAndScoringMatcher = TIME_AND_SCORING_PATTERN.matcher(spannable);
|
||||
while (timeAndScoringMatcher.find()) {
|
||||
falsePositives.add(Pair.create(timeAndScoringMatcher.start(), timeAndScoringMatcher.end()));
|
||||
}
|
||||
|
||||
Map<Pattern, Integer> emoticons = ANDROID_EMOTICONS;
|
||||
|
@ -1469,10 +1475,10 @@ public final class EmojiconHandler {
|
|||
}
|
||||
}
|
||||
if (set) {
|
||||
// check that found emojicon is not in an web url
|
||||
for (Pair<Integer, Integer> webUrl : webUrls) {
|
||||
if ((matcher.start() >= webUrl.first && matcher.start() <= webUrl.second)
|
||||
|| (matcher.end() >= webUrl.first && matcher.end() <= webUrl.second)) {
|
||||
// check that found emojicon is not in an web url or in a time or in a scoring
|
||||
for (Pair<Integer, Integer> falsePositive : falsePositives) {
|
||||
if ((matcher.start() >= falsePositive.first && matcher.start() <= falsePositive.second)
|
||||
|| (matcher.end() >= falsePositive.first && matcher.end() <= falsePositive.second)) {
|
||||
set = false;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.measite.minidns"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="9"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<application/>
|
||||
|
||||
</manifest>
|
|
@ -1,3 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
</lint>
|
|
@ -1,14 +0,0 @@
|
|||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-19
|
Binary file not shown.
|
@ -13,13 +13,10 @@ android {
|
|||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
testCompile 'junit:junit:4.12'
|
||||
compile 'com.android.support:appcompat-v7:22.2.1'
|
||||
}
|
||||
|
|
17
libs/thedevstacklogcat/proguard-rules.pro
vendored
17
libs/thedevstacklogcat/proguard-rules.pro
vendored
|
@ -1,17 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\Users\tzur\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
|
@ -1,20 +0,0 @@
|
|||
# To enable ProGuard in your project, edit project.properties
|
||||
# to define the proguard.config property as described in that file.
|
||||
#
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
|
@ -1,27 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /home/sam/android-sdk-linux/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
-dontwarn javax.naming.**
|
||||
|
||||
-keep class * extends java.util.ListResourceBundle {
|
||||
protected Object[][] getContents();
|
||||
}
|
||||
|
||||
-keepnames class * implements android.os.Parcelable {
|
||||
public static final ** CREATOR;
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
include ':libs:MemorizingTrustManager', ':libs:emojicon', ':libs:colorpicker', ':libs:SwipyRefreshLayout', ':libs:thedevstacklogcat'
|
||||
include ':libs:MemorizingTrustManager'
|
||||
include ':libs:emojicon'
|
||||
include ':libs:colorpicker'
|
||||
include ':libs:thedevstacklogcat'
|
||||
include ':libs:openpgp-api-lib'
|
||||
|
||||
rootProject.name = 'Conversations'
|
||||
|
|
|
@ -8,6 +8,8 @@ import android.preference.PreferenceManager;
|
|||
import java.io.File;
|
||||
|
||||
import de.thedevstack.conversationsplus.utils.ImageUtil;
|
||||
import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
|
||||
|
||||
import eu.siacs.conversations.R;
|
||||
|
||||
/**
|
||||
|
@ -19,6 +21,9 @@ public class ConversationsPlusApplication extends Application {
|
|||
*/
|
||||
private static ConversationsPlusApplication instance;
|
||||
|
||||
private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor();
|
||||
private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor();
|
||||
|
||||
/**
|
||||
* Initializes the application and saves its instance.
|
||||
*/
|
||||
|
@ -37,6 +42,14 @@ public class ConversationsPlusApplication extends Application {
|
|||
return ConversationsPlusApplication.instance;
|
||||
}
|
||||
|
||||
public static void executeFileAdding(Runnable r) {
|
||||
getInstance().mFileAddingExecutor.execute(r);
|
||||
}
|
||||
|
||||
public static void executeDatabaseOperation(Runnable r) {
|
||||
getInstance().mDatabaseExecutor.execute(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application's context.
|
||||
* @return Context the application's context
|
||||
|
|
|
@ -14,6 +14,14 @@ public class ConversationsPlusPreferences extends Settings {
|
|||
private static ConversationsPlusPreferences instance;
|
||||
private final SharedPreferences sharedPreferences;
|
||||
|
||||
public static String imgTransferFolder() {
|
||||
return getString("img_transfer_folder", getString("app_name", "Conversations+"));
|
||||
}
|
||||
|
||||
public static String fileTransferFolder() {
|
||||
return getString("file_transfer_folder", getString("app_name", "Conversations+"));
|
||||
}
|
||||
|
||||
public static UserDecision resizePicture() {
|
||||
return getEnumFromStringPref("resize_picture", UserDecision.ASK);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
package de.thedevstack.conversationsplus.exceptions;
|
||||
|
||||
public class FileCopyException extends Exception {
|
||||
public class FileCopyException extends UiException {
|
||||
private static final long serialVersionUID = -1010013599132881427L;
|
||||
private int resId;
|
||||
|
||||
public FileCopyException(int resId) {
|
||||
this.resId = resId;
|
||||
}
|
||||
public FileCopyException(int resId) {
|
||||
super(resId);
|
||||
}
|
||||
|
||||
public int getResId() {
|
||||
return resId;
|
||||
}
|
||||
public FileCopyException(int resId, Throwable e) {
|
||||
super(resId, e);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package de.thedevstack.conversationsplus.exceptions;
|
||||
|
||||
/**
|
||||
* Created by tzur on 15.12.2015.
|
||||
*/
|
||||
public class ImageResizeException extends UiException {
|
||||
private static final long serialVersionUID = -1010013599112881427L;
|
||||
|
||||
public ImageResizeException(int resId) {
|
||||
super(resId);
|
||||
}
|
||||
|
||||
public ImageResizeException(int resId, Throwable e) {
|
||||
super(resId, e);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package de.thedevstack.conversationsplus.exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be shown in UI.
|
||||
*/
|
||||
public class UiException extends Exception {
|
||||
private static final long serialVersionUID = -1010015239132881427L;
|
||||
private int resId;
|
||||
|
||||
public UiException(int resId) {
|
||||
this.resId = resId;
|
||||
}
|
||||
|
||||
public UiException(int resId, Throwable e) {
|
||||
super(e);
|
||||
this.resId = resId;
|
||||
}
|
||||
|
||||
public int getResId() {
|
||||
return resId;
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ public class MessageDetailsDialog extends AbstractAlertDialog {
|
|||
*/
|
||||
protected void displayFileInfo(View view, Message message) {
|
||||
if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) {
|
||||
Logging.d("messagedetailsfile", "File is stored in path: " + message.getRelativeFilePath());
|
||||
view.findViewById(R.id.dlgMsgDetFileTable).setVisibility(View.VISIBLE);
|
||||
if (null != message.getFileParams()) {
|
||||
Message.FileParams params = message.getFileParams();
|
||||
|
@ -140,7 +141,7 @@ public class MessageDetailsDialog extends AbstractAlertDialog {
|
|||
// Get own resource name -> What about msg written on other client?
|
||||
String me = conversation.getAccount().getJid().getResourcepart();
|
||||
// Get resource name of chat partner, if available
|
||||
String other = (message.getCounterpart().isBareJid()) ? "" : message.getCounterpart().getResourcepart();
|
||||
String other = (null == message.getCounterpart() || message.getCounterpart().isBareJid()) ? "" : message.getCounterpart().getResourcepart();
|
||||
Logging.d("MesageDialog", "Me: " + me + ", other: " + other);
|
||||
TextView sender = (TextView) view.findViewById(R.id.dlgMsgDetSender);
|
||||
TextView receipient = (TextView) view.findViewById(R.id.dlgMsgDetReceipient);
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
package de.thedevstack.conversationsplus.ui.listeners;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import de.thedevstack.android.logcat.Logging;
|
||||
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
|
||||
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
|
||||
import de.thedevstack.conversationsplus.enums.UserDecision;
|
||||
import de.thedevstack.conversationsplus.exceptions.UiException;
|
||||
import de.thedevstack.conversationsplus.utils.FileHelper;
|
||||
import de.thedevstack.conversationsplus.utils.ImageUtil;
|
||||
import de.thedevstack.conversationsplus.utils.MessageUtil;
|
||||
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.entities.Conversation;
|
||||
import eu.siacs.conversations.entities.DownloadableFile;
|
||||
import eu.siacs.conversations.entities.Message;
|
||||
import eu.siacs.conversations.persistance.FileBackend;
|
||||
import eu.siacs.conversations.services.XmppConnectionService;
|
||||
import eu.siacs.conversations.ui.UiCallback;
|
||||
import eu.siacs.conversations.ui.XmppActivity;
|
||||
|
@ -81,13 +95,88 @@ public class ResizePictureUserDecisionListener implements UserDecisionListener {
|
|||
@Override
|
||||
public void onYes() {
|
||||
this.showPrepareFileToast();
|
||||
xmppConnectionService.attachImageToConversation(this.conversation, this.uri, this.callback);
|
||||
final Message message;
|
||||
final boolean forceEncryption = ConversationsPlusPreferences.forceEncryption();
|
||||
if (conversation.getNextEncryption(forceEncryption) == Message.ENCRYPTION_PGP) {
|
||||
message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
|
||||
} else {
|
||||
message = new Message(conversation, "", conversation.getNextEncryption(forceEncryption));
|
||||
}
|
||||
message.setCounterpart(conversation.getNextCounterpart());
|
||||
message.setType(Message.TYPE_IMAGE);
|
||||
ConversationsPlusApplication.executeFileAdding(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Bitmap resizedAndRotatedImage = ImageUtil.resizeAndRotateImage(uri);
|
||||
DownloadableFile file = FileBackend.compressImageAndCopyToPrivateStorage(message, resizedAndRotatedImage);
|
||||
String filePath = file.getAbsolutePath();
|
||||
long imageSize = file.getSize();
|
||||
int imageWidth = resizedAndRotatedImage.getWidth();
|
||||
int imageHeight = resizedAndRotatedImage.getHeight();
|
||||
MessageUtil.updateMessageWithImageDetails(message, filePath, imageSize, imageWidth, imageHeight);
|
||||
if (conversation.getNextEncryption(forceEncryption) == Message.ENCRYPTION_PGP) {
|
||||
xmppConnectionService.getPgpEngine().encrypt(message, callback);
|
||||
} else {
|
||||
callback.success(message);
|
||||
}
|
||||
} catch (final UiException e) {
|
||||
Logging.e("pictureresizesending", "Error while sending resized picture. " + e.getMessage());
|
||||
callback.error(e.getResId(), message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNo() {
|
||||
this.showPrepareFileToast();
|
||||
xmppConnectionService.attachImageToConversationWithoutResizing(this.conversation, this.uri, this.callback);
|
||||
final Message message;
|
||||
final boolean forceEncryption = ConversationsPlusPreferences.forceEncryption();
|
||||
if (conversation.getNextEncryption(forceEncryption) == Message.ENCRYPTION_PGP) {
|
||||
message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
|
||||
} else {
|
||||
message = new Message(conversation, "", conversation.getNextEncryption(forceEncryption));
|
||||
}
|
||||
message.setCounterpart(conversation.getNextCounterpart());
|
||||
message.setType(Message.TYPE_IMAGE);
|
||||
ConversationsPlusApplication.executeFileAdding(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = ConversationsPlusApplication.getInstance().getContentResolver().openInputStream(uri);
|
||||
long imageSize = is.available();
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
int imageHeight = options.outHeight;
|
||||
int imageWidth = options.outWidth;
|
||||
String filePath = FileHelper.getRealPathFromUri(uri);
|
||||
MessageUtil.updateMessageWithImageDetails(message, filePath, imageSize, imageWidth, imageHeight);
|
||||
if (conversation.getNextEncryption(forceEncryption) == Message.ENCRYPTION_PGP) {
|
||||
xmppConnectionService.getPgpEngine().encrypt(message, callback);
|
||||
} else {
|
||||
callback.success(message);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Logging.e("picturesending", "File not found to send not resized. " + e.getMessage());
|
||||
callback.error(R.string.error_file_not_found, message);
|
||||
} catch (IOException e) {
|
||||
Logging.e("picturesending", "Error while sending not resized picture. " + e.getMessage());
|
||||
callback.error(R.string.error_io_exception, message);
|
||||
} finally {
|
||||
if (null != is) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
Logging.w("picturesending", "Error while closing stream for sending not resized picture. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -11,11 +11,16 @@ import android.util.LruCache;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import de.thedevstack.android.logcat.Logging;
|
||||
import de.thedevstack.conversationsplus.exceptions.FileCopyException;
|
||||
import de.thedevstack.conversationsplus.exceptions.ImageResizeException;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.entities.Message;
|
||||
import eu.siacs.conversations.persistance.FileBackend;
|
||||
import eu.siacs.conversations.utils.ExifHelper;
|
||||
|
@ -24,6 +29,8 @@ import eu.siacs.conversations.utils.ExifHelper;
|
|||
* This util provides
|
||||
*/
|
||||
public final class ImageUtil {
|
||||
|
||||
private static int IMAGE_SIZE = 1920;
|
||||
private static LruCache<String, Bitmap> BITMAP_CACHE;
|
||||
|
||||
/**
|
||||
|
@ -105,6 +112,59 @@ public final class ImageUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes and rotates an image given by uri and returns the bitmap.
|
||||
* @param image the uri of the image to be resized and rotated
|
||||
* @return resized and rotated bitmap
|
||||
* @throws ImageResizeException
|
||||
*/
|
||||
public static Bitmap resizeAndRotateImage(Uri image) throws ImageResizeException {
|
||||
return ImageUtil.resizeAndRotateImage(image, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes and rotates an image given by uri and returns the bitmap.
|
||||
* @param image the uri of the image to be resized and rotated
|
||||
* @return resized and rotated bitmap
|
||||
* @throws ImageResizeException
|
||||
*/
|
||||
private static Bitmap resizeAndRotateImage(Uri image, int sampleSize) throws ImageResizeException {
|
||||
InputStream imageInputStream = null;
|
||||
try {
|
||||
imageInputStream = StreamUtil.openInputStreamFromContentResolver(image);
|
||||
Bitmap originalBitmap;
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
int inSampleSize = (int) Math.pow(2, sampleSize);
|
||||
Logging.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize);
|
||||
options.inSampleSize = inSampleSize;
|
||||
originalBitmap = BitmapFactory.decodeStream(imageInputStream, null, options);
|
||||
imageInputStream.close();
|
||||
if (originalBitmap == null) {
|
||||
throw new ImageResizeException(R.string.error_not_an_image_file);
|
||||
}
|
||||
Bitmap scaledBitmap = ImageUtil.resize(originalBitmap, IMAGE_SIZE);
|
||||
int rotation = ImageUtil.getRotation(image);
|
||||
if (rotation > 0) {
|
||||
scaledBitmap = ImageUtil.rotate(scaledBitmap, rotation);
|
||||
}
|
||||
|
||||
return scaledBitmap;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ImageResizeException(R.string.error_file_not_found);
|
||||
} catch (IOException e) {
|
||||
throw new ImageResizeException(R.string.error_io_exception);
|
||||
} catch (OutOfMemoryError e) {
|
||||
++sampleSize;
|
||||
if (sampleSize <= 3) {
|
||||
return resizeAndRotateImage(image, sampleSize);
|
||||
} else {
|
||||
throw new ImageResizeException(R.string.error_out_of_memory);
|
||||
}
|
||||
} finally {
|
||||
StreamUtil.close(imageInputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rotation from the exif information of an image identified with the given uri.
|
||||
* The orientation is retrieved by parsing the stream of the image.
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
package de.thedevstack.conversationsplus.utils;
|
||||
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import eu.siacs.conversations.entities.DownloadableFile;
|
||||
import eu.siacs.conversations.entities.Message;
|
||||
import eu.siacs.conversations.persistance.FileBackend;
|
||||
|
||||
/**
|
||||
* Created by tzur on 15.12.2015.
|
||||
*/
|
||||
public final class MessageUtil {
|
||||
public static void updateMessageWithImageDetails(Message message, String filePath, long size, int imageWidth, int imageHeight) {
|
||||
message.setRelativeFilePath(filePath);
|
||||
MessageUtil.updateMessageBodyWithImageParams(message, size, imageWidth, imageHeight);
|
||||
}
|
||||
|
||||
public static void updateFileParams(Message message) {
|
||||
updateFileParams(message, null);
|
||||
}
|
||||
|
||||
public static void updateFileParams(Message message, URL url) {
|
||||
DownloadableFile file = FileBackend.getFile(message);
|
||||
int imageWidth = -1;
|
||||
int imageHeight = -1;
|
||||
if (message.getType() == Message.TYPE_IMAGE || file.getMimeType().startsWith("image/")) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||
imageHeight = options.outHeight;
|
||||
imageWidth = options.outWidth;
|
||||
}
|
||||
|
||||
MessageUtil.updateMessageBodyWithFileParams(message, url, file.getSize(), imageWidth, imageHeight);
|
||||
}
|
||||
|
||||
private static void updateMessageBodyWithFileParams(Message message, URL url, long fileSize, int imageWidth, int imageHeight) {
|
||||
message.setBody(MessageUtil.getMessageBodyWithImageParams(url, fileSize, imageWidth, imageHeight));
|
||||
}
|
||||
|
||||
private static void updateMessageBodyWithImageParams(Message message, long size, int imageWidth, int imageHeight) {
|
||||
MessageUtil.updateMessageBodyWithImageParams(message, null, size, imageWidth, imageHeight);
|
||||
}
|
||||
|
||||
private static void updateMessageBodyWithImageParams(Message message, URL url, long size, int imageWidth, int imageHeight) {
|
||||
message.setBody(MessageUtil.getMessageBodyWithImageParams(url, size, imageWidth, imageHeight));
|
||||
}
|
||||
|
||||
private static String getMessageBodyWithImageParams(URL url, long size, int imageWidth, int imageHeight) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (null != url) {
|
||||
sb.append(url.toString());
|
||||
sb.append('|');
|
||||
}
|
||||
sb.append(size);
|
||||
if (-1 < imageWidth) {
|
||||
sb.append('|');
|
||||
sb.append(imageWidth);
|
||||
}
|
||||
if (-1 < imageHeight) {
|
||||
sb.append('|');
|
||||
sb.append(imageHeight);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private MessageUtil() {
|
||||
// Static helper class
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ import org.openintents.openpgp.util.OpenPgpApi;
|
|||
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
|
||||
|
||||
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
|
||||
import de.thedevstack.conversationsplus.utils.MessageUtil;
|
||||
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
|
@ -103,7 +104,7 @@ public class PgpEngine {
|
|||
OpenPgpApi.RESULT_CODE_ERROR)) {
|
||||
case OpenPgpApi.RESULT_CODE_SUCCESS:
|
||||
URL url = message.getFileParams().url;
|
||||
FileBackend.updateFileParams(message, url);
|
||||
MessageUtil.updateFileParams(message, url);
|
||||
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
|
||||
PgpEngine.this.mXmppConnectionService
|
||||
.updateMessage(message);
|
||||
|
|
|
@ -17,6 +17,7 @@ import javax.net.ssl.SSLHandshakeException;
|
|||
|
||||
import de.thedevstack.android.logcat.Logging;
|
||||
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
|
||||
import de.thedevstack.conversationsplus.utils.MessageUtil;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.R;
|
||||
|
@ -237,7 +238,7 @@ public class HttpDownloadConnection implements Transferable {
|
|||
|
||||
private void updateImageBounds() {
|
||||
message.setType(Message.TYPE_FILE);
|
||||
FileBackend.updateFileParams(message, mUrl);
|
||||
MessageUtil.updateFileParams(message, mUrl);
|
||||
mXmppConnectionService.updateMessage(message);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.net.URL;
|
|||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
import de.thedevstack.android.logcat.Logging;
|
||||
import de.thedevstack.conversationsplus.utils.MessageUtil;
|
||||
import de.thedevstack.conversationsplus.utils.StreamUtil;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
|
@ -165,7 +166,7 @@ public class HttpUploadConnection implements Transferable {
|
|||
if (key != null) {
|
||||
mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key));
|
||||
}
|
||||
FileBackend.updateFileParams(message, mGetUrl);
|
||||
MessageUtil.updateFileParams(message, mGetUrl);
|
||||
message.setTransferable(null);
|
||||
message.setCounterpart(message.getConversation().getJid().toBareJid());
|
||||
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
|
||||
|
|
|
@ -20,8 +20,8 @@ import android.webkit.MimeTypeMap;
|
|||
|
||||
import de.thedevstack.android.logcat.Logging;
|
||||
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
|
||||
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
|
||||
import de.thedevstack.conversationsplus.exceptions.FileCopyException;
|
||||
import de.thedevstack.conversationsplus.utils.ImageUtil;
|
||||
import de.thedevstack.conversationsplus.utils.StreamUtil;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
|
@ -32,8 +32,6 @@ import eu.siacs.conversations.entities.Message;
|
|||
|
||||
public final class FileBackend {
|
||||
|
||||
private static int IMAGE_SIZE = 1920;
|
||||
|
||||
private static final SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
|
||||
|
||||
public static DownloadableFile getFile(Message message) {
|
||||
|
@ -64,29 +62,35 @@ public final class FileBackend {
|
|||
return new DownloadableFile(path);
|
||||
} else {
|
||||
if (Arrays.asList(Transferable.VALID_IMAGE_EXTENSIONS).contains(extension)) {
|
||||
return new DownloadableFile(getConversationsFileDirectory() + path);
|
||||
} else {
|
||||
return new DownloadableFile(getConversationsImageDirectory() + path);
|
||||
} else {
|
||||
return new DownloadableFile(getConversationsFileDirectory() + path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getConversationsFileDirectory() {
|
||||
return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Conversations/";
|
||||
return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + ConversationsPlusPreferences.fileTransferFolder() + File.separator;
|
||||
}
|
||||
|
||||
public static String getConversationsImageDirectory() {
|
||||
return Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_PICTURES).getAbsolutePath()
|
||||
+ "/Conversations/";
|
||||
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath() + File.separator + ConversationsPlusPreferences.imgTransferFolder() + File.separator;
|
||||
}
|
||||
|
||||
private static String getPrivateFileDirectoryPath() {
|
||||
return ConversationsPlusApplication.getPrivateFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
private static String getPrivateImageDirectoryPath() {
|
||||
return FileBackend.getPrivateFileDirectoryPath() + File.separator + "Images" + File.separator;
|
||||
}
|
||||
|
||||
public static DownloadableFile copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
|
||||
Logging.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage");
|
||||
String mime = ConversationsPlusApplication.getInstance().getContentResolver().getType(uri);
|
||||
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
|
||||
message.setRelativeFilePath(message.getUuid() + "." + extension);
|
||||
message.setRelativeFilePath(FileBackend.getPrivateFileDirectoryPath() + message.getUuid() + "." + extension);
|
||||
DownloadableFile file = getFile(message);
|
||||
file.getParentFile().mkdirs();
|
||||
OutputStream os = null;
|
||||
|
@ -114,68 +118,30 @@ public final class FileBackend {
|
|||
return file;
|
||||
}
|
||||
|
||||
public static DownloadableFile copyImageToPrivateStorage(Message message, Uri image)
|
||||
throws FileCopyException {
|
||||
return copyImageToPrivateStorage(message, image, 0);
|
||||
}
|
||||
|
||||
private static DownloadableFile copyImageToPrivateStorage(Message message,
|
||||
Uri image, int sampleSize) throws FileCopyException {
|
||||
public static DownloadableFile compressImageAndCopyToPrivateStorage(Message message, Bitmap scaledBitmap) throws FileCopyException {
|
||||
message.setRelativeFilePath(FileBackend.getPrivateImageDirectoryPath() + message.getUuid() + ".webp");
|
||||
DownloadableFile file = getFile(message);
|
||||
file.getParentFile().mkdirs();
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
file.createNewFile();
|
||||
is = StreamUtil.openInputStreamFromContentResolver(image);
|
||||
os = new FileOutputStream(file);
|
||||
|
||||
Bitmap originalBitmap;
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
int inSampleSize = (int) Math.pow(2, sampleSize);
|
||||
Logging.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize);
|
||||
options.inSampleSize = inSampleSize;
|
||||
originalBitmap = BitmapFactory.decodeStream(is, null, options);
|
||||
is.close();
|
||||
if (originalBitmap == null) {
|
||||
throw new FileCopyException(R.string.error_not_an_image_file);
|
||||
}
|
||||
Bitmap scaledBitmap = ImageUtil.resize(originalBitmap, IMAGE_SIZE);
|
||||
int rotation = ImageUtil.getRotation(image);
|
||||
if (rotation > 0) {
|
||||
scaledBitmap = ImageUtil.rotate(scaledBitmap, rotation);
|
||||
}
|
||||
os = new FileOutputStream(file);
|
||||
|
||||
boolean success = scaledBitmap.compress(Bitmap.CompressFormat.WEBP, 75, os);
|
||||
if (!success) {
|
||||
throw new FileCopyException(R.string.error_compressing_image);
|
||||
}
|
||||
os.flush();
|
||||
long size = file.getSize();
|
||||
int width = scaledBitmap.getWidth();
|
||||
int height = scaledBitmap.getHeight();
|
||||
message.setBody(Long.toString(size) + '|' + width + '|' + height);
|
||||
return file;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new FileCopyException(R.string.error_file_not_found);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new FileCopyException(R.string.error_io_exception);
|
||||
} catch (IOException e) {
|
||||
throw new FileCopyException(R.string.error_io_exception, e);
|
||||
} catch (SecurityException e) {
|
||||
throw new FileCopyException(R.string.error_security_exception_during_image_copy);
|
||||
} catch (OutOfMemoryError e) {
|
||||
++sampleSize;
|
||||
if (sampleSize <= 3) {
|
||||
return copyImageToPrivateStorage(message, image, sampleSize);
|
||||
} else {
|
||||
throw new FileCopyException(R.string.error_out_of_memory);
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
throw new FileCopyException(R.string.error_security_exception_during_image_copy);
|
||||
} catch (NullPointerException e) {
|
||||
throw new FileCopyException(R.string.error_io_exception);
|
||||
} finally {
|
||||
StreamUtil.close(os);
|
||||
StreamUtil.close(is);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public static Uri getTakePhotoUri() {
|
||||
|
@ -196,33 +162,6 @@ public final class FileBackend {
|
|||
return Uri.parse("file://" + file.getAbsolutePath());
|
||||
}
|
||||
|
||||
public static void updateFileParams(Message message) {
|
||||
updateFileParams(message,null);
|
||||
}
|
||||
|
||||
public static void updateFileParams(Message message, URL url) {
|
||||
DownloadableFile file = getFile(message);
|
||||
if (message.getType() == Message.TYPE_IMAGE || file.getMimeType().startsWith("image/")) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||
int imageHeight = options.outHeight;
|
||||
int imageWidth = options.outWidth;
|
||||
if (url == null) {
|
||||
message.setBody(Long.toString(file.getSize()) + '|' + imageWidth + '|' + imageHeight);
|
||||
} else {
|
||||
message.setBody(url.toString()+"|"+Long.toString(file.getSize()) + '|' + imageWidth + '|' + imageHeight);
|
||||
}
|
||||
} else {
|
||||
if (url != null) {
|
||||
message.setBody(url.toString()+"|"+Long.toString(file.getSize()));
|
||||
} else {
|
||||
message.setBody(Long.toString(file.getSize()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean isFileAvailable(Message message) {
|
||||
return getFile(message).exists();
|
||||
}
|
||||
|
|
|
@ -54,10 +54,13 @@ import de.thedevstack.android.logcat.Logging;
|
|||
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
|
||||
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
|
||||
import de.thedevstack.conversationsplus.exceptions.FileCopyException;
|
||||
import de.thedevstack.conversationsplus.exceptions.UiException;
|
||||
import de.thedevstack.conversationsplus.utils.AvatarUtil;
|
||||
import de.thedevstack.conversationsplus.utils.FileHelper;
|
||||
import de.thedevstack.conversationsplus.utils.ImageUtil;
|
||||
import de.thedevstack.conversationsplus.utils.MessageUtil;
|
||||
import de.tzur.conversations.Settings;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.crypto.PgpEngine;
|
||||
|
@ -86,7 +89,6 @@ import eu.siacs.conversations.utils.ExceptionHelper;
|
|||
import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
|
||||
import eu.siacs.conversations.utils.PRNGFixes;
|
||||
import eu.siacs.conversations.utils.PhoneHelper;
|
||||
import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
|
||||
import eu.siacs.conversations.utils.Xmlns;
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
import eu.siacs.conversations.xmpp.OnBindListener;
|
||||
|
@ -110,6 +112,7 @@ import eu.siacs.conversations.xmpp.pep.Avatar;
|
|||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
|
||||
|
||||
import me.leolin.shortcutbadger.ShortcutBadger;
|
||||
|
||||
public class XmppConnectionService extends Service implements OnPhoneContactsLoadedListener {
|
||||
|
@ -130,9 +133,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||
}
|
||||
};
|
||||
|
||||
private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor();
|
||||
private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor();
|
||||
|
||||
private final IBinder mBinder = new XmppConnectionBinder();
|
||||
private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
|
||||
private final FileObserver fileObserver = new FileObserver(
|
||||
|
@ -375,111 +375,34 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||
message.setCounterpart(conversation.getNextCounterpart());
|
||||
message.setType(Message.TYPE_FILE);
|
||||
String path = FileHelper.getRealPathFromUri(uri);
|
||||
if (path!=null) {
|
||||
if (path != null) {
|
||||
message.setRelativeFilePath(path);
|
||||
FileBackend.updateFileParams(message);
|
||||
MessageUtil.updateFileParams(message);
|
||||
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
|
||||
getPgpEngine().encrypt(message, callback);
|
||||
} else {
|
||||
callback.success(message);
|
||||
}
|
||||
} else {
|
||||
mFileAddingExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
FileBackend.copyFileToPrivateStorage(message, uri);
|
||||
FileBackend.updateFileParams(message);
|
||||
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
|
||||
getPgpEngine().encrypt(message, callback);
|
||||
} else {
|
||||
callback.success(message);
|
||||
}
|
||||
} catch (FileCopyException e) {
|
||||
callback.error(e.getResId(), message);
|
||||
}
|
||||
}
|
||||
});
|
||||
ConversationsPlusApplication.executeFileAdding(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
FileBackend.copyFileToPrivateStorage(message, uri);
|
||||
MessageUtil.updateFileParams(message);
|
||||
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
|
||||
getPgpEngine().encrypt(message, callback);
|
||||
} else {
|
||||
callback.success(message);
|
||||
}
|
||||
} catch (FileCopyException e) {
|
||||
callback.error(e.getResId(), message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void attachImageToConversationWithoutResizing(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
|
||||
final Message message;
|
||||
final boolean forceEncryption = ConversationsPlusPreferences.forceEncryption();
|
||||
if (conversation.getNextEncryption(forceEncryption) == Message.ENCRYPTION_PGP) {
|
||||
message = new Message(conversation, "",
|
||||
Message.ENCRYPTION_DECRYPTED);
|
||||
} else {
|
||||
message = new Message(conversation, "",
|
||||
conversation.getNextEncryption(forceEncryption));
|
||||
}
|
||||
message.setCounterpart(conversation.getNextCounterpart());
|
||||
message.setType(Message.TYPE_IMAGE);
|
||||
mFileAddingExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = ConversationsPlusApplication.getInstance().getContentResolver().openInputStream(uri);
|
||||
long imageSize = is.available();
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
int imageHeight = options.outHeight;
|
||||
int imageWidth = options.outWidth;
|
||||
message.setRelativeFilePath(FileHelper.getRealPathFromUri(uri));
|
||||
message.setBody(Long.toString(imageSize) + '|' + imageWidth + '|' + imageHeight);
|
||||
callback.success(message);
|
||||
} catch (FileNotFoundException e) {
|
||||
Logging.e("pictureresize", "File not found to send not resized. " + e.getMessage());
|
||||
callback.error(R.string.error_file_not_found, message);
|
||||
} catch (IOException e) {
|
||||
Logging.e("pictureresize", "Error while sending not resized picture. " + e.getMessage());
|
||||
callback.error(R.string.error_io_exception, message);
|
||||
} finally {
|
||||
if (null != is) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
Logging.w("pictureresize", "Error while closing stream for sending not resized picture. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void attachImageToConversation(final Conversation conversation,
|
||||
final Uri uri, final UiCallback<Message> callback) {
|
||||
final Message message;
|
||||
final boolean forceEncryption = ConversationsPlusPreferences.forceEncryption();
|
||||
if (conversation.getNextEncryption(forceEncryption) == Message.ENCRYPTION_PGP) {
|
||||
message = new Message(conversation, "",
|
||||
Message.ENCRYPTION_DECRYPTED);
|
||||
} else {
|
||||
message = new Message(conversation, "",
|
||||
conversation.getNextEncryption(forceEncryption));
|
||||
}
|
||||
message.setCounterpart(conversation.getNextCounterpart());
|
||||
message.setType(Message.TYPE_IMAGE);
|
||||
mFileAddingExecutor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
FileBackend.copyImageToPrivateStorage(message, uri);
|
||||
if (conversation.getNextEncryption(forceEncryption) == Message.ENCRYPTION_PGP) {
|
||||
getPgpEngine().encrypt(message, callback);
|
||||
} else {
|
||||
callback.success(message);
|
||||
}
|
||||
} catch (final FileCopyException e) {
|
||||
callback.error(e.getResId(), message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Conversation find(Bookmark bookmark) {
|
||||
return find(bookmark.getAccount(), bookmark.getJid());
|
||||
}
|
||||
|
@ -1019,7 +942,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||
updateConversationUi();
|
||||
}
|
||||
};
|
||||
mDatabaseExecutor.execute(runnable);
|
||||
ConversationsPlusApplication.executeDatabaseOperation(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1123,7 +1046,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||
}
|
||||
}
|
||||
};
|
||||
mDatabaseExecutor.execute(runnable);
|
||||
ConversationsPlusApplication.executeDatabaseOperation(runnable);
|
||||
}
|
||||
|
||||
public List<Account> getAccounts() {
|
||||
|
@ -2390,8 +2313,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||
databaseBackend.writeRoster(account.getRoster());
|
||||
}
|
||||
};
|
||||
mDatabaseExecutor.execute(runnable);
|
||||
|
||||
ConversationsPlusApplication.executeDatabaseOperation(runnable);
|
||||
}
|
||||
|
||||
public List<String> getKnownHosts() {
|
||||
|
|
|
@ -14,6 +14,7 @@ import android.os.SystemClock;
|
|||
|
||||
import de.thedevstack.android.logcat.Logging;
|
||||
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
|
||||
import de.thedevstack.conversationsplus.utils.MessageUtil;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
|
@ -93,7 +94,7 @@ public class JingleConnection implements Transferable {
|
|||
JingleConnection.this.mXmppConnectionService
|
||||
.getNotificationService().push(message);
|
||||
}
|
||||
FileBackend.updateFileParams(message);
|
||||
MessageUtil.updateFileParams(message);
|
||||
mXmppConnectionService.databaseBackend.createMessage(message);
|
||||
mXmppConnectionService.markMessage(message,
|
||||
Message.STATUS_RECEIVED);
|
||||
|
|
|
@ -505,4 +505,9 @@
|
|||
<string name="cplus_copied_to_clipboard">In Zwischenablage kopiert</string>
|
||||
<string name="pref_show_logcat_title">Zeige logcat Ausgabe</string>
|
||||
<string name="pref_show_logcat_summary">Zeigt die Ausgabe von logcat an. Hilfreich für die Fehlersuche.</string>
|
||||
<string name="pref_file_transfer">Ordnername, um eingehnde Datein zu speichern</string>
|
||||
<string name="pref_file_transfer_folder_summary">Unterordner des globalen Dateiordners, um eingehende Dateien zu speichern.</string>
|
||||
<string name="pref_img_file_transfer">Ordnername, um eingehende Bilder zu speichern</string>
|
||||
<string name="pref_img_file_transfer_summary">Unterordner des globalen Bilderordners, um eingehende Bilder zu speichern.</string>
|
||||
<string name="pref_file_transfer_category">Dateiübertragung</string>
|
||||
</resources>
|
||||
|
|
|
@ -538,4 +538,9 @@
|
|||
<string name="pref_show_logcat_title">Show logcat output</string>
|
||||
<string name="pref_show_logcat_summary">Shows the output of logcat. This is useful for debugging.</string>
|
||||
<string name="cplus_bugreport_jabberid">c+bugs@conference.thedevstack.de</string>
|
||||
<string name="pref_file_transfer">Folder to save incoming files</string>
|
||||
<string name="pref_file_transfer_folder_summary">This is the subdirectory for incoming files.</string>
|
||||
<string name="pref_img_file_transfer">Folder to save incoming pictures</string>
|
||||
<string name="pref_img_file_transfer_summary">This is the subdirectory in the pictures directory for incoming files.</string>
|
||||
<string name="pref_file_transfer_category">File Transfer</string>
|
||||
</resources>
|
||||
|
|
|
@ -17,6 +17,26 @@
|
|||
android:summary="@string/pref_xmpp_resource_summary"
|
||||
android:title="@string/pref_xmpp_resource" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="2"
|
||||
android:entries="@array/confirm_strings"
|
||||
android:entryValues="@array/confirm_values"
|
||||
android:key="confirm_messages_list"
|
||||
android:summary="@string/pref_confirm_messages_summary"
|
||||
android:title="@string/pref_confirm_messages" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="chat_states"
|
||||
android:summary="@string/pref_chat_states_summary"
|
||||
android:title="@string/pref_chat_states" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="parse_emoticons"
|
||||
android:summary="@string/pref_parse_emoticons_summary"
|
||||
android:title="@string/pref_parse_emoticons"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/pref_file_transfer_category">
|
||||
<PreferenceScreen
|
||||
android:summary="@string/pref_accept_files_summary"
|
||||
android:title="@string/pref_accept_files">
|
||||
|
@ -42,6 +62,16 @@
|
|||
android:title="@string/pref_accept_files_download" />
|
||||
|
||||
</PreferenceScreen>
|
||||
<EditTextPreference
|
||||
android:title="@string/pref_img_file_transfer"
|
||||
android:summary="@string/pref_file_transfer_folder_summary"
|
||||
android:key="file_transfer_folder"
|
||||
android:defaultValue="Conversations+"/>
|
||||
<EditTextPreference
|
||||
android:title="@string/pref_file_transfer"
|
||||
android:summary="@string/pref_img_file_transfer_summary"
|
||||
android:key="img_transfer_folder"
|
||||
android:defaultValue="Conversations+"/>
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="ASK"
|
||||
|
@ -50,25 +80,6 @@
|
|||
android:key="resize_picture"
|
||||
android:summary="@string/pref_resize_picture_summary"
|
||||
android:title="@string/pref_resize_picture"/>
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="2"
|
||||
android:entries="@array/confirm_strings"
|
||||
android:entryValues="@array/confirm_values"
|
||||
android:key="confirm_messages_list"
|
||||
android:summary="@string/pref_confirm_messages_summary"
|
||||
android:title="@string/pref_confirm_messages" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="chat_states"
|
||||
android:summary="@string/pref_chat_states_summary"
|
||||
android:title="@string/pref_chat_states" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="parse_emoticons"
|
||||
android:summary="@string/pref_parse_emoticons_summary"
|
||||
android:title="@string/pref_parse_emoticons"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/pref_notification_settings" >
|
||||
<PreferenceScreen
|
||||
|
|
Loading…
Reference in a new issue