SharedPreferences:加密

import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;

import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * 加密Preference数据的类
 */
public class SecurePreferences {

    public static class SecurePreferencesException extends RuntimeException {

        public SecurePreferencesException(Throwable e) {
            super(e);
        }

    }

    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
    private static final String KEY_TRANSFORMATION = "AES/ECB/PKCS5Padding";
    private static final String SECRET_KEY_HASH_TRANSFORMATION = "SHA-256";
    private static final String CHARSET = "UTF-8";

    private final boolean encryptKeys;
    private final Cipher writer;
    private final Cipher reader;
    private final Cipher keyWriter;
    private final SharedPreferences preferences;

    /**
     * This will initialize an instance of the SecurePreferences class
     * 
     * @param context
     *            your current context.
     * @param preferenceName
     *            name of preferences file (preferenceName.xml)
     * @param secureKey
     *            the key used for encryption, finding a good key scheme is
     *            hard. Hardcoding your key in the application is bad, but
     *            better than plaintext preferences. Having the user enter the
     *            key upon application launch is a safe(r) alternative, but
     *            annoying to the user.
     * @param encryptKeys
     *            settings this to false will only encrypt the values, true will
     *            encrypt both values and keys. Keys can contain a lot of
     *            information about the plaintext value of the value which can
     *            be used to decipher the value.
     */
    public SecurePreferences(Context context, String preferenceName, String secureKey, boolean encryptKeys)
            throws SecurePreferencesException {
        try {
            this.writer = Cipher.getInstance(TRANSFORMATION);
            this.reader = Cipher.getInstance(TRANSFORMATION);
            this.keyWriter = Cipher.getInstance(KEY_TRANSFORMATION);

            initCiphers(secureKey);

            this.preferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE);

            this.encryptKeys = encryptKeys;
        } catch (GeneralSecurityException e) {
            throw new SecurePreferencesException(e);
        } catch (UnsupportedEncodingException e) {
            throw new SecurePreferencesException(e);
        }
    }

    protected void initCiphers(String secureKey) throws UnsupportedEncodingException, NoSuchAlgorithmException,
            InvalidKeyException, InvalidAlgorithmParameterException {
        IvParameterSpec ivSpec = getIv();
        SecretKeySpec secretKey = getSecretKey(secureKey);

        writer.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
        reader.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
        keyWriter.init(Cipher.ENCRYPT_MODE, secretKey);
    }

    protected IvParameterSpec getIv() {
        byte[] iv = new byte[writer.getBlockSize()];
        System.arraycopy("fldsjfodasjifudslfjdsaofshaufihadsf".getBytes(), 0, iv, 0, writer.getBlockSize());
        return new IvParameterSpec(iv);
    }

    protected SecretKeySpec getSecretKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        byte[] keyBytes = createKeyBytes(key);
        return new SecretKeySpec(keyBytes, TRANSFORMATION);
    }

    protected byte[] createKeyBytes(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance(SECRET_KEY_HASH_TRANSFORMATION);
        md.reset();
        byte[] keyBytes = md.digest(key.getBytes(CHARSET));
        return keyBytes;
    }

    public void put(String key, String value) {
        if (value == null) {
            preferences.edit().remove(toKey(key)).commit();
        } else {
            putValue(toKey(key), value);
        }
    }

    public boolean containsKey(String key) {
        return preferences.contains(toKey(key));
    }

    public void removeValue(String key) {
        preferences.edit().remove(toKey(key)).commit();
    }

    public String getString(String key) throws SecurePreferencesException {
        if (preferences.contains(toKey(key))) {
            String securedEncodedValue = preferences.getString(toKey(key), "");
            return decrypt(securedEncodedValue);
        }
        return null;
    }

    public void clear() {
        preferences.edit().clear().commit();
    }

    private String toKey(String key) {
        if (encryptKeys)
            return encrypt(key, keyWriter);
        else
            return key;
    }

    private void putValue(String key, String value) throws SecurePreferencesException {
        String secureValueEncoded = encrypt(value, writer);

        preferences.edit().putString(key, secureValueEncoded).commit();
    }

    protected String encrypt(String value, Cipher writer) throws SecurePreferencesException {
        byte[] secureValue;
        try {
            secureValue = convert(writer, value.getBytes(CHARSET));
        } catch (UnsupportedEncodingException e) {
            throw new SecurePreferencesException(e);
        }
        String secureValueEncoded = Base64.encodeToString(secureValue, Base64.NO_WRAP);
        return secureValueEncoded;
    }

    protected String decrypt(String securedEncodedValue) {
        byte[] securedValue = Base64.decode(securedEncodedValue, Base64.NO_WRAP);
        byte[] value = convert(reader, securedValue);
        try {
            return new String(value, CHARSET);
        } catch (UnsupportedEncodingException e) {
            throw new SecurePreferencesException(e);
        }
    }

    private static byte[] convert(Cipher cipher, byte[] bs) throws SecurePreferencesException {
        try {
            return cipher.doFinal(bs);
        } catch (Exception e) {
            throw new SecurePreferencesException(e);
        }
    }
}

URL处理:解析

import android.net.Uri;

import java.net.URLDecoder;
import java.util.regex.Pattern;

public class Parser {
        protected static final Pattern PAT_QUERY =
            Pattern.compile("\\?(([a-z0-9-._~]|%[0-9a-f]{2}|[!$&'()*+,;=]|[:@]|[/?]|[/#])*)", Pattern.CASE_INSENSITIVE);

    public Parser() {
    }

    public static String getQueryParameter(String url, String key){

        try {
            int qsi = url.indexOf('?', url.indexOf(':'));
            String query = url.substring(qsi + 1);

            final String encodedKey = Uri.encode(key, null);
            final int length = query.length();
            int start = 0;
            do {
                int nextAmpersand = query.indexOf('&', start);
                int end = nextAmpersand != -1 ? nextAmpersand : length;

                int separator = query.indexOf('=', start);
                if (separator > end || separator == -1) {
                    separator = end;
                }

                if (separator - start == encodedKey.length()
                        && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
                    if (separator == end) {
                        return "";
                    } else {
                        String encodedValue = query.substring(separator + 1, end);
                        return URLDecoder.decode(encodedValue, "utf-8");
                    }
                }

                // Move start to end of name.
                if (nextAmpersand != -1) {
                    start = nextAmpersand + 1;
                } else {
                    break;
                }
            } while (true);

        }catch (Exception e){

        }

        return null;
    }

    public class Components {
        public String scheme_ = null;
        public String userinfo_ = null;
        public String host_ = null;
        public HostType hostType_ = null;
        public Integer port_ = null;
        public String[] path_ = null;
        public Boolean pathAbsolute_ = null;
        public String query_ = null;
        public String fragment_ = null;
    }

    public enum HostType {
        IPV4, IPV6, IPVFUTURE, REG_NAME
    }
}

位操作:高、中、低8位处理

public class BitwiseOperateUtil {

    /**
     * 计算结果
     * @param top 高16位
     * @param middle 中8位
     * @param mantissa 尾8位
     * @return
     */
    public static int calSourceFromMask(int top,int middle,int mantissa){
        return (top << 16) | (middle << 8) | mantissa;
    }

    /**
     * 获取指定位
     * @param n 原值
     * @param m 位数
     * @return
     */
    int getBit(int n, int m){
        return (n >> (m-1)) & 1;
    }

    /**
     * 获取高16位
     * @param value
     * @return
     */
    private int getTopSixteen(int value){
        return value >> 16;
    }

    /**
     * 获取中8位
     * @param value
     * @return
     */
    private int getMidEight(int value){
        return getMattissaEight(value >> 8);
    }

    /**
     * 获取低8位
     * @param value
     * @return
     */
    private int getMattissaEight(int value){
        return (value & 255);
    }
}

MD5:字符串和文件

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Md5Utils {

    //十六进制
    private static final char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

    /**
     * byte数组转换为转换为16进制
     * @param b 数据
     * @return 返回字符串
     */
    private static String toHexString(byte[] b) {
        StringBuilder sb = new StringBuilder(b.length * 2);
        for (int i = 0; i < b.length; i++) {
            sb.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]);
            sb.append(HEX_DIGITS[b[i] & 0x0f]);
        }
        return sb.toString();
    }


    /**
     * MD5对字符串校验
     * @param string
     * @return
     */
    public static String md5(String string) {
        byte[] bytes;
        try {
            bytes = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 should be supported?", e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("UTF-8 should be supported?", e);
        }

        return toHexString(bytes);
    }

    /**
     * 取文件MD5值
     * @param filename 文件路径
     * @return MD5值
     */
    public static String fileMD5(String filename) {
        InputStream fis;
        byte[] buffer = new byte[1024];
        int numRead = 0;
        MessageDigest md5;
        try {
            fis = new FileInputStream(filename);
            md5 = MessageDigest.getInstance("MD5");
            while ((numRead = fis.read(buffer)) > 0) {
                md5.update(buffer, 0, numRead);
            }
            fis.close();
            return toHexString(md5.digest());
        } catch (Exception e) {
            System.out.println("error");
            return null;
        }
    }
}

Zip使用:压缩和解压缩“文件”

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;


public class ZipFileUtil {

    private static final int BUFF_SIZE = 1024;

    private static ExecutorService mExecutor = Executors.newCachedThreadPool();

    public static void unzipFile(File file, String folderPath, UnZipProgress progress){
        mExecutor.execute(new UnZipTask(file, folderPath, progress));
    }

    public interface UnZipProgress{
        int STATUS_COMPLETE = 0;
        int STATUS_FAILURE = 1;
        void progress(int status, int progress);
    }

    private static class UnZipTask implements Runnable {

        private File mZipFile;
        private String mFolderPath;
        private UnZipProgress mUnZipProgress;

        UnZipTask(File zipFile, String folderPath, UnZipProgress progress) {
            mZipFile = zipFile;
            mFolderPath = folderPath;
            mUnZipProgress = progress;
        }

        public void unzipFile(File zipFile, String folderPath) throws ZipException, IOException {
            File desDir = new File(folderPath);
            if (!desDir.exists()) {
                desDir.mkdirs();
            }
            ZipFile zf = new ZipFile(zipFile);
            for (Enumeration<?> entries = zf.entries(); entries.hasMoreElements();) {
                ZipEntry entry = ((ZipEntry)entries.nextElement());
                if (entry.isDirectory()) {
                    continue;
                }
                InputStream in = zf.getInputStream(entry);
                String str = folderPath + File.separator + entry.getName();
                str = new String(str.getBytes(), "utf-8");
                File desFile = new File(str);
                if (!desFile.exists()) {
                    File fileParentDir = desFile.getParentFile();
                    if (!fileParentDir.exists()) {
                        fileParentDir.mkdirs();
                    }
                    desFile.createNewFile();
                }
                OutputStream out = new FileOutputStream(desFile);
                byte buffer[] = new byte[BUFF_SIZE];
                int realLength;
                while ((realLength = in.read(buffer)) > 0) {
                    out.write(buffer, 0, realLength);
                }
                in.close();
                out.close();
            }
            zf.close();
            if(mUnZipProgress != null){
                mUnZipProgress.progress(UnZipProgress.STATUS_COMPLETE, 0);
            }
        }

        @Override
        public void run() {
            try {
                unzipFile(mZipFile, mFolderPath);
            }catch (Exception e) {
                if(mUnZipProgress != null){
                    mUnZipProgress.progress(UnZipProgress.STATUS_FAILURE, 0);
                }
                e.printStackTrace();
            }
        }
    }
}

Zip使用:压缩和解压缩“字符串”

 

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class ZipUtils {
   private static final String LOG_TAG = "ZipUtils";

   /*
    * 用标准的Zip包压缩数据
    */
   public static String compress(String str){
      try{
         if (str == null || str.length() == 0) {
            return str;
         }
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         GZIPOutputStream gzip = new GZIPOutputStream(out);
         gzip.write(str.getBytes());
         gzip.close();
         return out.toString("ISO-8859-1");
      } catch (Exception e) {
         Logger.i(LOG_TAG, "IOException,Failed to compress1 message."+ e.toString());
      }
      return null;
   }
   
   // 解压缩
   public static String uncompress(String str){
      try{
      if (str == null || str.length() == 0) {
         return str;
      }
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes("ISO-8859-1"));
      GZIPInputStream gunzip = new GZIPInputStream(in);
      byte[] buffer = new byte[256];
      int n;
      while((n = gunzip.read(buffer))>=0)
      {
         out.write(buffer,0,n);
      }
      return out.toString();
      }
      catch(Exception e)
      {
         Logger.i(LOG_TAG, "IOException,Failed to uncompress1 message."+ e.toString());
      }
      return null;
   }
}

 

解决 Could not find com.android.tools.build:gradle:3.0.0-alpha1 in circle ci

解决 Could not find com.android.tools.build:gradle:3.0.0-alpha1 in circle ci

如下:

buildscript {
    repositories {
        jcenter()
        maven {
            url 'https://maven.google.com'
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0-alpha1'
        classpath 'com.tencent.bugly:symtabfileuploader:latest.release'
    }
}

修改成

buildscript {
    repositories {
        jcenter()
        maven {
            url 'https://dl.google.com/dl/android/maven2'
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0-alpha1'
        classpath 'com.tencent.bugly:symtabfileuploader:latest.release'
    }
}

https://blog.v9gg.com/myweishanli/article/623

SimpleDateFormat使用详解

SimpleDateFormat 是一个以国别敏感的方式格式化和分析数据的具体类。 它允许格式化 (date -> text)、语法分析 (text -> date)和标准化。

SimpleDateFormat函数语法:

G 年代标志符
y 年
M 月
d 日
h 时 在上午或下午 (1~12)
H 时 在一天中 (0~23)
m 分
s 秒
S 毫秒
E 星期
D 一年中的第几天
F 一月中第几个星期几
w 一年中第几个星期
W 一月中第几个星期
a 上午 / 下午 标记符
k 时 在一天中 (1~24)
K 时 在上午或下午 (0~11)
z 时区

 

public static void main(String[] args) {
SimpleDateFormat myFmt=new SimpleDateFormat(“yyyy年MM月dd日 HH时mm分ss秒”);
SimpleDateFormat myFmt1=new SimpleDateFormat(“yy/MM/dd HH:mm”);
SimpleDateFormat myFmt2=new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);//等价于now.toLocaleString()
SimpleDateFormat myFmt3=new SimpleDateFormat(“yyyy年MM月dd日 HH时mm分ss秒 E “);
SimpleDateFormat myFmt4=new SimpleDateFormat(
“一年中的第 D 天 一年中第w个星期 一月中第W个星期 在一天中k时 z时区”);
Date now=new Date();
System.out.println(myFmt.format(now));
System.out.println(myFmt1.format(now));
System.out.println(myFmt2.format(now));
System.out.println(myFmt3.format(now));
System.out.println(myFmt4.format(now));
System.out.println(now.toGMTString());
System.out.println(now.toLocaleString());
System.out.println(now.toString());
}

2004年12月16日 17时24分27秒
04/12/16 17:24
2004-12-16 17:24:27
2004年12月16日 17时24分27秒 星期四
一年中的第 351 天 一年中第51个星期 一月中第3个星期 在一天中17时 CST时区
16 Dec 2004 09:24:27 GMT
2004-12-16 17:24:27
Thu Dec 16 17:24:27 CST 2004

AndroidManifest 合并出错 tools:replace=”android:allowBackup”

出现的问题: Manifest merger failed : Attribute application@allowBackup value=(false) from AndroidManifest.xml:116:9-36
is also present at [manager:common:unspecified:release] AndroidManifest.xml:12:9-35 value=(true).

Suggestion: add ‘tools:replace=”android:allowBackup”‘ to <application> element at AndroidManifest.xml:114:5-1136:19 to override.

解决方法:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="包名">

在application节点添加

<application tools:replace="android:allowBackup"  ......

同样的还有
tools:replace=”android:icon, android:theme,android:allowBackup”