android-Ultra-Pull-To-Refresh动画效果简要分析

#android-Ultra-Pull-To-Refresh动画效果简要分析

android-Ultra-Pull-To-Refresh

image

核心类StoreHousePath
package in.srain.cube.views.ptr.header;

import android.util.SparseArray;

import java.util.ArrayList;

/**
* Created by srain on 11/7/14.
*/
public class StoreHousePath {

private static final SparseArray<float[]> sPointList;

static {
sPointList = new SparseArray<float[]>();
float[][] LETTERS = new float[][]{
new float[]{
// A
24, 0, 1, 22,
1, 22, 1, 72,
24, 0, 47, 22,
47, 22, 47, 72,
1, 48, 47, 48
},

new float[]{
// B
0, 0, 0, 72,
0, 0, 37, 0,
37, 0, 47, 11,
47, 11, 47, 26,
47, 26, 38, 36,
38, 36, 0, 36,
38, 36, 47, 46,
47, 46, 47, 61,
47, 61, 38, 71,
37, 72, 0, 72,
},

new float[]{
// C
47, 0, 0, 0,
0, 0, 0, 72,
0, 72, 47, 72,
},

new float[]{
// D
0, 0, 0, 72,
0, 0, 24, 0,
24, 0, 47, 22,
47, 22, 47, 48,
47, 48, 23, 72,
23, 72, 0, 72,
},

new float[]{
// E
0, 0, 0, 72,
0, 0, 47, 0,
0, 36, 37, 36,
0, 72, 47, 72,
},

new float[]{
// F
0, 0, 0, 72,
0, 0, 47, 0,
0, 36, 37, 36,
},

new float[]{
// G
47, 23, 47, 0,
47, 0, 0, 0,
0, 0, 0, 72,
0, 72, 47, 72,
47, 72, 47, 48,
47, 48, 24, 48,
},

new float[]{
// H
0, 0, 0, 72,
0, 36, 47, 36,
47, 0, 47, 72,
},

new float[]{
// I
0, 0, 47, 0,
24, 0, 24, 72,
0, 72, 47, 72,
},

new float[]{
// J
47, 0, 47, 72,
47, 72, 24, 72,
24, 72, 0, 48,
},

new float[]{
// K
0, 0, 0, 72,
47, 0, 3, 33,
3, 38, 47, 72,
},

new float[]{
// L
0, 0, 0, 72,
0, 72, 47, 72,
},

new float[]{
// M
0, 0, 0, 72,
0, 0, 24, 23,
24, 23, 47, 0,
47, 0, 47, 72,
},

new float[]{
// N
0, 0, 0, 72,
0, 0, 47, 72,
47, 72, 47, 0,
},

new float[]{
// O
0, 0, 0, 72,
0, 72, 47, 72,
47, 72, 47, 0,
47, 0, 0, 0,
},

new float[]{
// P
0, 0, 0, 72,
0, 0, 47, 0,
47, 0, 47, 36,
47, 36, 0, 36,
},

new float[]{
// Q
0, 0, 0, 72,
0, 72, 23, 72,
23, 72, 47, 48,
47, 48, 47, 0,
47, 0, 0, 0,
24, 28, 47, 71,
},

new float[]{
// R
0, 0, 0, 72,
0, 0, 47, 0,
47, 0, 47, 36,
47, 36, 0, 36,
0, 37, 47, 72,
},

new float[]{
// S
47, 0, 0, 0,
0, 0, 0, 36,
0, 36, 47, 36,
47, 36, 47, 72,
47, 72, 0, 72,
},

new float[]{
// T
0, 0, 47, 0,
24, 0, 24, 72,
},

new float[]{
// U
0, 0, 0, 72,
0, 72, 47, 72,
47, 72, 47, 0,
},

new float[]{
// V
0, 0, 24, 72,
24, 72, 47, 0,
},

new float[]{
// W
0, 0, 0, 72,
0, 72, 24, 49,
24, 49, 47, 72,
47, 72, 47, 0
},

new float[]{
// X
0, 0, 47, 72,
47, 0, 0, 72
},

new float[]{
// Y
0, 0, 24, 23,
47, 0, 24, 23,
24, 23, 24, 72
},

new float[]{
// Z
0, 0, 47, 0,
47, 0, 0, 72,
0, 72, 47, 72
},
};
final float[][] NUMBERS = new float[][]{
new float[]{
// 0
0, 0, 0, 72,
0, 72, 47, 72,
47, 72, 47, 0,
47, 0, 0, 0,
},
new float[]{
// 1
24, 0, 24, 72,
},

new float[]{
// 2
0, 0, 47, 0,
47, 0, 47, 36,
47, 36, 0, 36,
0, 36, 0, 72,
0, 72, 47, 72
},

new float[]{
// 3
0, 0, 47, 0,
47, 0, 47, 36,
47, 36, 0, 36,
47, 36, 47, 72,
47, 72, 0, 72,
},

new float[]{
// 4
0, 0, 0, 36,
0, 36, 47, 36,
47, 0, 47, 72,
},

new float[]{
// 5
0, 0, 0, 36,
0, 36, 47, 36,
47, 36, 47, 72,
47, 72, 0, 72,
0, 0, 47, 0
},

new float[]{
// 6
0, 0, 0, 72,
0, 72, 47, 72,
47, 72, 47, 36,
47, 36, 0, 36
},

new float[]{
// 7
0, 0, 47, 0,
47, 0, 47, 72
},

new float[]{
// 8
0, 0, 0, 72,
0, 72, 47, 72,
47, 72, 47, 0,
47, 0, 0, 0,
0, 36, 47, 36
},

new float[]{
// 9
47, 0, 0, 0,
0, 0, 0, 36,
0, 36, 47, 36,
47, 0, 47, 72,
}
};
// A - Z
for (int i = 0; i < LETTERS.length; i++) {
sPointList.append(i + 65, LETTERS[i]);
}
// a - z
for (int i = 0; i < LETTERS.length; i++) {
sPointList.append(i + 65 + 32, LETTERS[i]);
}
// 0 - 9
for (int i = 0; i < NUMBERS.length; i++) {
sPointList.append(i + 48, NUMBERS[i]);
}
// blank
addChar(' ', new float[]{});
// -
addChar('-', new float[]{
0, 36, 47, 36
});
// .
addChar('.', new float[]{
24, 60, 24, 72
});
}

public static void addChar(char c, float[] points) {
sPointList.append(c, points);
}

public static ArrayList<float[]> getPath(String str) {
return getPath(str, 1, 14);
}

/**
* @param str
* @param scale
* @param gapBetweenLetter
* @return ArrayList of float[] {x1, y1, x2, y2}
*/
public static ArrayList<float[]> getPath(String str, float scale, int gapBetweenLetter) {
ArrayList<float[]> list = new ArrayList<float[]>();
float offsetForWidth = 0;
for (int i = 0; i < str.length(); i++) {
int pos = str.charAt(i);
int key = sPointList.indexOfKey(pos);
if (key == -1) {
continue;
}
float[] points = sPointList.get(pos);
int pointCount = points.length / 4;

for (int j = 0; j < pointCount; j++) {
float[] line = new float[4];
for (int k = 0; k < 4; k++) {
float l = points[j * 4 + k];
// x
if (k % 2 == 0) {
line[k] = (l + offsetForWidth) * scale;
}
// y
else {
line[k] = l * scale;
}
}
list.add(line);
}
offsetForWidth += 57 + gapBetweenLetter;
}
return list;
}
}

对数字和坐标敏感的话,看到上面这几个数组应该就能理解这部分的作用.
这里将 数字 和 字母 分解成 对应笔画的 Path , 每2个数字 作为一个坐标点
每一行的2个坐标点作为一个笔画

所以,该库不支持 这里定义之外的 其它字符 比如汉字 某些特殊符号之类的…
虽然提供了一个方法让你以xml的形式的自定义path进来…但是需要自定义path还是算了…

作为库分析来说…其实看到这里就已经猜到后面的实现方式了……

顺藤摸瓜在 StoreHouseHeader里找到了对 StoreHousePath 的调用
这里先把path封装成了 StoreHouseBarItem 对象方便处理

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    float progress = mProgress;
    int c1 = canvas.save();
    int len = mItemList.size();

    for (int i = 0; i < len; i++) {

        canvas.save();
        StoreHouseBarItem storeHouseBarItem = mItemList.get(i);
        float offsetX = mOffsetX + storeHouseBarItem.midPoint.x;
        float offsetY = mOffsetY + storeHouseBarItem.midPoint.y;

        if (mIsInLoading) {
            storeHouseBarItem.getTransformation(getDrawingTime(), mTransformation);
            canvas.translate(offsetX, offsetY);
        } else {

            if (progress == 0) {
                storeHouseBarItem.resetPosition(horizontalRandomness);
                continue;
            }

            float startPadding = (1 - internalAnimationFactor) * i / len;
            float endPadding = 1 - internalAnimationFactor - startPadding;

            // done
            if (progress == 1 || progress >= 1 - endPadding) {
                canvas.translate(offsetX, offsetY);
                storeHouseBarItem.setAlpha(mBarDarkAlpha);
            } else {
                float realProgress;
                if (progress <= startPadding) {
                    realProgress = 0;
                } else {
                    realProgress = Math.min(1, (progress - startPadding) / internalAnimationFactor);
                }
                offsetX += storeHouseBarItem.translationX * (1 - realProgress);
                offsetY += -mDropHeight * (1 - realProgress);
                Matrix matrix = new Matrix();
                matrix.postRotate(360 * realProgress);
                matrix.postScale(realProgress, realProgress);
                matrix.postTranslate(offsetX, offsetY);
                storeHouseBarItem.setAlpha(mBarDarkAlpha * realProgress);
                canvas.concat(matrix);
            }
        }
        storeHouseBarItem.draw(canvas);
        canvas.restore();
    }
    if (mIsInLoading) {
        invalidate();
    }
    canvas.restoreToCount(c1);
}


//StoreHouseBarItem中对应的draw方法
public void draw(Canvas canvas) {
    canvas.drawLine(mCStartPoint.x, mCStartPoint.y, mCEndPoint.x, mCEndPoint.y, mPaint);
}

根据startPadding 和 progress的值对每个笔画进行平移 旋转 缩放 以及 alpha值的调整.

注意这里startPadding实际上是通过笔画在数组中的位置计算出来的…

所以简单来看 每一个笔画的 realProgress 是不同的
realProgress 受 笔画在数组中的位置 i 和 progress 影响


剩下个高光效果直接用Shader渲染的..不分析了…..

Folder-DrawerLayout 开发日志2

folder-drawer-layout

folder-drawer-layout

弧形褶皱添加完毕…..有些bug….

数值上也需要调整一下….需要构造一个 x=f(y)

当 y=0 或 y=height 时 f(y) = 0 的函数 。。

本来是写了个二次函数。。但是二次函数是对称的。。只能保证中点在 height/2时满足条件….形状和斜率太对称很违和,不够自然…

可能写2个2次函数拼起来也可以….反正y轴上其实只有6个采样点…..都是插值插出来的….曲线不光滑也看不出来……….


另外一个隐患是函数太复杂了。。。本来是为了清晰起见把每个步骤都分开计算。。

循环上有些冗余。。。但是对比图形计算的部分。。浪费的性能应该不在一个数量级上。。

但是类似这样的代码

 private float[] applyScaleXEffect(float offset) {
        for (int i = 0; i < 6; i++)
            for (int j = 0; j < 51; j++) {
                meshVerts[i * 102 + 2 * j] = meshVerts[i * 102 + 2 * j]
                        * (1.0f + 0.0f * offset) ;

                meshVerts[i * 102 + 2 * j]=(offset) * meshVerts[i * 102 + 2 * j] *  (1+ (meshVerts[i * 102 + 2 * j+1]-1000)*(meshVerts[i * 102 + 2 * j+1]-1000)/10000 /width);
            }
        return meshVerts;
    }

可阅读性已经基本没有了。。。。考虑到性能问题。。。最后可能还要把冗余的循环计算都合并起来。。。最后可能变成一个超长的公式。。。。。。。估计要做到一次成型。。然后再也不维护了。。

可阅读性要求很高。。。但是对性能又很敏感。。。。。我想想到底要怎么处理这个结构。。。

如果证实冗余的循环对性能影响不大的话。。。还是尽量把每个步骤分开写吧。。。优化的问题扔最后了

数值调整完以后要赶紧把 能拆出来的变量拆出来,能写成常量的写成常量。。不然过几天鬼还记得这个10000是怎么算出来的….

顺便发现用Google看函数图挑形状真是方便..

google

Folder-DrawerLayout 开发日志1

 

folder-drawer-layout

动态效果加进去了…..

效果意外的恶心…..y轴的偏移看起来要换个 函数 重新构筑一下…

而且如果矩阵是预设的…..对应的偏移节点方程 其实可以算好了。。。再加个数组直接存在里面吧…

最后再用复杂界面测吧….估计效率是坨shit…..毕竟是 5*50矩阵….

泛型函数

今天看到一黑科技….

泛型类 泛型参数倒是很常用…

用在返回值类型上的泛型函数我还是第一次见到这么玩的…

写起代码来大概是这样

public class MainActivity extends Activity{
    private Button mButton;
    private TextView mTextView;
    private ListView mListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mButton = $(R.id.button);
        mTextView = $(R.id.textview);
        mListView = $(R.id.listview);

    }

    public <T extends View> T $(int id)
    {
        return (T) super.findViewById(id);
    }
}

一种JQuery的感觉。。。主要是这个$伪装成操作符的样子很有迷惑性…当然JQuery里的$本身也是这样实现的…..

好处大概是代码压缩的厉害。。省掉了强制类型转换。。写起代码来清晰一些。。

使用起来方便。。一共才1行代码。。扔BaseActivity里就好了。。

当然功能很弱逼。。。比注解形的弱多了。。。

Folder-DrawerLayout 开发日志0

folder-drawer-layout
morning-routine

又仔细看了下MorningRoutine….发现还有个渐变阴影…

目测应该是在挂verts之前用shader先渲染在原图上的

x轴看起来也不是均匀的….首

大概先是要把波长写成x相关的函数多代换一层…..

然后这个挤压效果还要把x轴总长按y轴位置等比压缩一下….

原效果verts矩阵是 5*15的…棱角还比较大…..不知道加大verts点数对性能有多大影响…

记录一下,元旦再弄了…有点蛋疼……一次要把这么多玩意载入脑子里面….一旦被interrupt就完了…看来是只适合半夜写了…

ClientProtocolException / CircularRedirectException

ClientProtocolException / CircularRedirectException

redirect

CircularRedirectException 循环重定向
其实并没有真的发生循环重定向

三个请求顺序如下:

https://stage.ajinga.com/v1/client-jobs/2680

–>

http://stage.ajinga.com/m/client-jobs/2680/

–>

https://stage.ajinga.com/m/client-jobs/2680/

由于第2个和第3个请求 地址完全一致,
httpclient错误的判定其为 循环重定向.

当然实际上 第一次 redirect就该把请求重定向到 https://stage.ajinga.com/m/client-jobs/2680/

但是server错误的返回了
http://stage.ajinga.com/m/client-jobs/2680/

由于server不支持非ssl方式连接,自动触发了重定向,又将其重定向至https://stage.ajinga.com/m/client-jobs/2680/

逻辑上也没大问题,就是多了一次请求的时间.

通过设置参数

httpClient.getHttpClient().getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);

可以ignore 循环重定向问题.

但是本质上 server第一次就应当返回正确的https方式的redirect地址.

How to soleve eclipse refresh external folder

How to soleve eclipse refresh external folder

According to the stackoverflow:

If you have references to external sources put them in a zip file:
YourProject->rightClick->Properties->Java Build Path->libraries->…, and then most notably android.jar, but other libs can be the culprit too. Expand it and and select Source attachment, and then (if it doesn’t say ‘None’) press the ‘Edit…’-button. If that points to a directory waht you should do is compress that source-directory into a zip file and make the source attachment point to that file.Apparently eclipse/adt feels the need to refresh sources on the file-system. When they’re in a zip-file it seems confident that they have not changed….

Just unattached source code is OK.

SecureRandom changed in latest Android version(maybe since 4.2)

使用以前的自己写的一个AES加密工具类的时候发现跑不通了,调用会直接crash,并抛出

11-11 09:05:16.747: W/System.err(13832): java.security.NoSuchAlgorithmException: SecureRandom AES implementation not found

源代码如下:

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Locale;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
* 
* @author Peter.Ding
* 
*/public class AESTools {
    private static AESTools instance = new AESTools();
    private String password = "peter.ding@augmentum.com";
    private KeyGenerator kgen;

    private AESTools() {
    }

    public static AESTools getInstance() {
        return instance;
    }

    public String encrypt(String content) {
        try {
            kgen = KeyGenerator.getInstance("AES");
            Provider p = Security.getProvider("BC");
            SecureRandom s = SecureRandom.getInstance("AES", p);

            //The right code 
            //Provider p = Security.getProvider("Crypto"); 
            //SecureRandom s = SecureRandom.getInstance("SHA1PRNG", p);

            s.setSeed(password.getBytes());
            kgen.init(128, s);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            byte[] byteContent = content.getBytes("utf-8");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] result = cipher.doFinal(byteContent);
            return bytesToHexString(result);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String decrypt(String str) {
        byte[] content = hexStringToByte(str);
        try {
            kgen = KeyGenerator.getInstance("AES");
            Provider p = Security.getProvider("BC");
            SecureRandom s = SecureRandom.getInstance("AES", p);

            //The right code 
            //Provider p = Security.getProvider("Crypto"); 
            //SecureRandom s = SecureRandom.getInstance("SHA1PRNG", p);

            s.setSeed(password.getBytes());
            kgen.init(128, s);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] result = cipher.doFinal(content);
            System.out.println(bytesToHexString(result));
            return new String(result, "utf-8");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length &lt;= 0) {
            return null;
        }
        for (int i = 0; i &lt; src.length; i++) {
            int v = src[i] &amp; 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() &lt; 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString().toUpperCase(Locale.ENGLISH);
    }

    public static byte[] hexStringToByte(String hex) {
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();
        for (int i = 0; i &lt; len; i++) {
            int pos = i * 2;
            result[i] = (byte) (toByte(achar[pos]) &lt;&lt; 4 | toByte(achar[pos + 1]));
        }
        return result;
    }

    private static byte toByte(char c) {
        byte b = (byte) "0123456789ABCDEF".indexOf(c);
        return b;
    }

}

以上代码是我12年的时候为 加密数据库写的,我马上意识到肯定是Android又修改了Security Provider,当时2.1 2.3适配把我坑了一次,4.0适配又把我坑了一次,没想到还要坑我第三次…..无语…

Provider p = Security.getProvider("BC");
SecureRandom s = SecureRandom.getInstance("AES", p);

报错的就是上面这行代码,果断复制粘贴百度

结果搜出来结果就是这个 http://dkmeteor.iteye.com/blog/1394887

仔细一看还是我2012年写的blog…. 算了….不看也知道肯定是 Provider和SecureRandom又改动了…

直接拉Provider列表

Android's OpenSSL-backed security provider
1ASN.1, DER, PkiPath, PKCS7
BouncyCastle Security Provider v1.49
HARMONY (SHA1 digest; SecureRandom; SHA1withDSA signature)
Harmony JSSE Provider
Android KeyStore security provider

http://www.bouncycastle.org/ 翻了一下文档 BouncyCastle 是支持AES的,不知为何直接调用会抛NoSuchAlgorithmException 没看到关于Android内Provider支持的Algorithm具体列表.

试了下使用 Android OpenSSL (2.3以下版本不支持) , 一样会抛出该错误,最后想了想改成原始版本的默认值 SHA1PRNG和Crypto. Done. (2.3+版本默认Default Provider修改成了 Android OpenSSL)


后来又找到一篇 Security Enhancements in Jelly Bean 的文档

http://android-developers.blogspot.com/2013/02/security-enhancements-in-jelly-bean.html

原文在墙外,关键部分我复制保存以下

New implementation of SecureRandom

Android 4.2 includes a new default implementation of SecureRandom based on OpenSSL. In the older Bouncy Castle-based implementation, given a known seed, SecureRandom could technically (albeit incorrectly) be treated as a source of deterministic data. With the new OpenSSL-based implementation, this is no longer possible.

In general, the switch to the new SecureRandom implementation should be transparent to apps. However, if your app is relying on SecureRandom to generate deterministic data, such as keys for encrypting data, you may need to modify this area of your app. For example, if you have been using SecureRandom to retrieve keys for encrypting/decrypting content, you will need to find another means of doing that.

A recommended approach is to generate a truly random AES key upon first launch and store that key in internal storage. For more information, see the post “Using Cryptography to Store Credentials Safely”.
还有另一篇关于 最佳实践的

http://android-developers.blogspot.com/2013/02/using-cryptography-to-store-credentials.html

 

The right way

A more reasonable approach is simply to generate a truly random AES key when an application is first launched:

public static SecretKey generateKey() throws NoSuchAlgorithmException {
// Generate a 256-bit key
final int outputKeyLength = 256;

SecureRandom secureRandom = new SecureRandom();
// Do not seed secureRandom! Automatically seeded from system entropy.
KeyGenerator keyGenerator = KeyGenerator.getInstance(“AES”);
keyGenerator.init(outputKeyLength, secureRandom);
SecretKey key = keyGenerator.generateKey();
return key;
}
Note that the security of this approach relies on safeguarding the generated key, which is is predicated on the security of the internal storage. Leaving the target file unencrypted (but set to MODE_PRIVATE) would provide similar security.

Even more security
If your app needs additional encryption, a recommended approach is to require a passphase or PIN to access your application. This passphrase could be fed into PBKDF2 to generate the encryption key. (PBKDF2 is a commonly used algorithm for deriving key material from a passphrase, using a technique known as “key stretching”.) Android provides an implementation of this algorithm inside SecretKeyFactory as PBKDF2WithHmacSHA1:

public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
// Number of PBKDF2 hardening rounds to use. Larger values increase
// computation time. You should select a value that causes computation
// to take >100ms.
final int iterations = 1000;

// Generate a 256-bit key
final int outputKeyLength = 256;

SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA1”);
KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
return secretKey;
}
The salt should be a random string, again generated using SecureRandom and persisted on internal storage alongside any encrypted data. This is important to mitigate the risk of attackers using a rainbow table to precompute password hashes.
因为使用了固定的seed作为随机种子生成器(RNG)的参数,导致生成的随机数其实是固定的,所以官方不推荐这种用法,当然我类库里的seed其实就是 加密密码,如果不需要指定密码的话, 倒是可以用官方的推荐的这种方式.

 

manifest xml bug with android.intent.action.MAIN

manifest xml bug

使用如下配置

<intent-filter>
    <action android:name=“android.intent.action.VIEW” />
    <action android:name=“android.intent.action.MAIN” />
    <category android:name=“android.intent.category.LAUNCHER” />
</intent-filter>

在Eclipse上Run, 可以正常安装,并启动应用 若使用下面这个配置(只是调换了VIEW和MAIN的顺序)

<intent-filter>
    <action android:name=“android.intent.action.MAIN” />
    <action android:name=“android.intent.action.VIEW” />
    <category android:name=“android.intent.category.LAUNCHER” />
</intent-filter>

在Eclipse上Run后,提示

“No Launcher activity found!

The launch will only sync the application package on the device!”

虽然也能安装成功,但是程序不会自动启动

——————————————————————————-

目测是Eclipse自身的bug, 蛋疼.

GoogleMap without Google Play Service

今天在QQ群里看到有人研究 地图的问题.

目前由于 国内阉割 Google Play Service的问题,大部分解决方案是

国内相关的 用 百度地图

国外相关的 用 Google地图

然后分开处理………..

然后有人提到 携程 的app可以在没有 Google Play Service的情况下调起Google Map

于是专门下载下来测试了一下,确实可以。

但是使用 UI Automator 把View Tree拉下来的看的时候

很明显可以发现这其实是一个 WebView

uiautomator-xiecheng

所以猜测携程 其实通过WebView调用移动版Google地图的方式使用的Google Map.

不过这样就要走js去控制这里面的逻辑………..不过也可能是Server端做好了直接载入进来的…