├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── example │ │ └── huangzj │ │ └── dbencrypt │ │ ├── LogUtil.java │ │ ├── MainActivity.java │ │ ├── MyApplication.java │ │ └── dao │ │ ├── DatabaseEncrypted.java │ │ ├── DatabaseHelper.java │ │ ├── bean │ │ ├── City.java │ │ └── CityDao.java │ │ └── ormlite │ │ ├── DaoListener.java │ │ ├── DaoObserver.java │ │ ├── DaoOperation.java │ │ ├── DaoThreadMode.java │ │ └── OrmLiteDao.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── maven_user.properties ├── ormlite-sqlcipher.iml ├── settings.gradle ├── sqlcipher-old ├── .gitignore ├── build.gradle ├── libs │ ├── armeabi │ │ ├── libdatabase_sqlcipher.so │ │ ├── libsqlcipher_android.so │ │ └── libstlport_shared.so │ ├── guava-r09.jar │ └── sqlcipher.jar ├── maven_pom.properties ├── maven_upload.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── icudt46l.zip │ ├── java │ └── com │ │ └── j256 │ │ └── ormlite │ │ └── sqlcipher │ │ ├── android │ │ ├── AndroidCompiledStatement.java │ │ ├── AndroidConnectionSource.java │ │ ├── AndroidDatabaseConnection.java │ │ ├── AndroidDatabaseResults.java │ │ ├── AndroidLog.java │ │ ├── DatabaseTableConfigUtil.java │ │ ├── apptools │ │ │ ├── OpenHelperManager.java │ │ │ ├── OrmLiteBaseActivity.java │ │ │ ├── OrmLiteBaseActivityGroup.java │ │ │ ├── OrmLiteBaseListActivity.java │ │ │ ├── OrmLiteBaseService.java │ │ │ ├── OrmLiteBaseTabActivity.java │ │ │ ├── OrmLiteConfigUtil.java │ │ │ └── OrmLiteSqliteOpenHelper.java │ │ └── compat │ │ │ ├── ApiCompatibility.java │ │ │ ├── ApiCompatibilityUtils.java │ │ │ ├── BasicApiCompatibility.java │ │ │ └── JellyBeanApiCompatibility.java │ │ └── db │ │ └── SqliteAndroidDatabaseType.java │ └── res │ └── values │ └── strings.xml └── sqlcipher ├── .gitignore ├── build.gradle ├── maven_pom.properties ├── maven_upload.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── j256 │ └── ormlite │ └── sqlcipher │ ├── DatabaseEncrypted.java │ ├── android │ ├── AndroidCompiledStatement.java │ ├── AndroidConnectionSource.java │ ├── AndroidDatabaseConnection.java │ ├── AndroidDatabaseResults.java │ ├── AndroidLog.java │ ├── DatabaseTableConfigUtil.java │ ├── apptools │ │ ├── OpenHelperManager.java │ │ ├── OrmLiteBaseActivity.java │ │ ├── OrmLiteBaseActivityGroup.java │ │ ├── OrmLiteBaseListActivity.java │ │ ├── OrmLiteBaseService.java │ │ ├── OrmLiteBaseTabActivity.java │ │ ├── OrmLiteConfigUtil.java │ │ └── OrmLiteSqliteOpenHelper.java │ └── compat │ │ ├── ApiCompatibility.java │ │ ├── ApiCompatibilityUtils.java │ │ ├── BasicApiCompatibility.java │ │ └── JellyBeanApiCompatibility.java │ └── db │ └── SqliteAndroidDatabaseType.java └── res └── values └── strings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | ormlite-sqlcipher -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.7 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ormlite-sqlcipher 2 | 3 | android应用ormlite与sqlcipher结合的本地数据库,ormlit提供便利的数据库操作,sqlcipher对数据库进行加密。 4 | 5 | sqlcipher源码参照https://github.com/wobuaihuangjun/android-database-sqlcipher 6 | 7 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply from: '../config.gradle' 3 | 4 | android { 5 | compileSdkVersion compileSdkVer 6 | buildToolsVersion buildToolsVer 7 | 8 | defaultConfig { 9 | applicationId "com.example.huangzj.dbencrypt" 10 | minSdkVersion minSdkVer 11 | targetSdkVersion targetSdkVer 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | 19 | // 只编译armeabi类型的so文件 20 | ndk { 21 | abiFilters = ["armeabi"] 22 | } 23 | 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | 29 | sourceSets { 30 | main { 31 | jni.srcDirs = [] 32 | jniLibs.srcDirs = ['src/main/jniLibs'] 33 | } 34 | } 35 | } 36 | 37 | repositories { 38 | flatDir { 39 | dirs 'libs' //this way we can find the .aar file in libs folder 40 | } 41 | } 42 | 43 | dependencies { 44 | 45 | compile project(':sqlcipher') 46 | } 47 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\android-sdk-windows-r23/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/huangzj/dbencrypt/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.huangzj.dbencrypt; 2 | 3 | import android.app.Activity; 4 | import android.database.Cursor; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.widget.TextView; 8 | 9 | import com.example.huangzj.dbencrypt.dao.DatabaseHelper; 10 | import com.example.huangzj.dbencrypt.dao.bean.City; 11 | import com.example.huangzj.dbencrypt.dao.bean.CityDao; 12 | 13 | import net.sqlcipher.database.SQLiteDatabase; 14 | 15 | import java.io.File; 16 | import java.util.List; 17 | 18 | public class MainActivity extends Activity { 19 | 20 | public static final String TAG = "MainActivity"; 21 | 22 | private TextView way1; 23 | private TextView way2; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | 29 | setContentView(R.layout.activity_main); 30 | 31 | way1 = (TextView) findViewById(R.id.way1); 32 | way2 = (TextView) findViewById(R.id.way2); 33 | 34 | testWay2(); 35 | 36 | dbIsUpdate(); 37 | } 38 | 39 | private void dbIsUpdate() { 40 | File unencryptedDatabase = MyApplication.getContext().getDatabasePath(DatabaseHelper.DATABASE_NAME); 41 | if (unencryptedDatabase.exists()) { 42 | Log.e(TAG, "unencrypted database is exist"); 43 | } 44 | 45 | File encryptedDatabase = MyApplication.getContext().getDatabasePath(DatabaseHelper.ENCRYPTED_DATABASE_NAME); 46 | if (encryptedDatabase.exists()) { 47 | Log.e(TAG, "encrypted database is exist"); 48 | } 49 | 50 | // Log.i(TAG, "数据库名:" + DatabaseHelper.DATABASE_NAME); 51 | // SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase( 52 | // DatabaseHelper.DATABASE_NAME, "", null); 53 | // Log.i(TAG, "加密的数据库数据"); 54 | // Cursor cursor = database.rawQuery("select * from city", new String[]{}); 55 | // cursor.moveToFirst(); 56 | // String a = cursor.getString(cursor.getColumnIndex("cityName")); 57 | // Log.d(TAG, a); 58 | // cursor.close(); 59 | } 60 | 61 | private void testWay2() { 62 | CityDao cityDao = new CityDao(this); 63 | 64 | List list = cityDao.queryForAll(); 65 | 66 | String cityNo = "0"; 67 | 68 | if (list != null) { 69 | cityNo = list.size() + ""; 70 | } 71 | 72 | City city = new City(); 73 | city.setCityNo(cityNo); 74 | city.setProvinceNo("5"); 75 | city.setCityName("深圳"); 76 | 77 | if (cityDao.insert(city)) { 78 | way2.append("\n"); 79 | way2.append("存入记录成功:" + city.toString()); 80 | } 81 | 82 | 83 | city = cityDao.queryByCityNo(cityNo); 84 | way2.append("\n"); 85 | way2.append(city.toString()); 86 | 87 | 88 | list = cityDao.queryForAll(); 89 | if (list != null) { 90 | way2.append("\n"); 91 | way2.append("数据count:" + list.size()); 92 | } 93 | 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/huangzj/dbencrypt/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.huangzj.dbencrypt; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.example.huangzj.dbencrypt.dao.DatabaseEncrypted; 7 | import com.example.huangzj.dbencrypt.dao.DatabaseHelper; 8 | import com.example.huangzj.dbencrypt.dao.bean.City; 9 | import com.example.huangzj.dbencrypt.dao.bean.CityDao; 10 | 11 | import net.sqlcipher.database.SQLiteDatabase; 12 | 13 | import java.util.List; 14 | 15 | 16 | /** 17 | * Created by huangzj on 2015/12/21. 18 | */ 19 | public class MyApplication extends Application { 20 | 21 | public static final String TAG = "MyApplication"; 22 | private static MyApplication instance; 23 | 24 | @Override 25 | public void onCreate() { 26 | super.onCreate(); 27 | Log.i(TAG, "3"); 28 | instance = this; 29 | 30 | initDb(); 31 | 32 | // testCity(); 33 | } 34 | 35 | private void initDb() { 36 | SQLiteDatabase.loadLibs(this); 37 | 38 | // DatabaseEncrypted.importUnencryptedDatabase( 39 | // this, 40 | // DatabaseHelper.DATABASE_NAME, 41 | // DatabaseHelper.ENCRYPTED_DATABASE_NAME, 42 | // DatabaseHelper.PASSWORD); 43 | 44 | DatabaseEncrypted.exportToUnencryptedDatabase(this, 45 | DatabaseHelper.ENCRYPTED_DATABASE_NAME, 46 | DatabaseHelper.DATABASE_NAME, 47 | DatabaseHelper.PASSWORD); 48 | } 49 | 50 | private void testCity() { 51 | Log.e(TAG, "testCity"); 52 | CityDao cityDao = new CityDao(this); 53 | List list = cityDao.queryForAll(); 54 | if (list != null) 55 | Log.e(TAG, "数据count:" + list.size()); 56 | } 57 | 58 | public static MyApplication getContext() { 59 | return instance; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/huangzj/dbencrypt/dao/DatabaseEncrypted.java: -------------------------------------------------------------------------------- 1 | package com.example.huangzj.dbencrypt.dao; 2 | 3 | import android.app.Application; 4 | import android.database.Cursor; 5 | import android.util.Log; 6 | 7 | import net.sqlcipher.database.SQLiteDatabase; 8 | 9 | import java.io.File; 10 | 11 | 12 | /** 13 | * 数据库加密 14 | */ 15 | public class DatabaseEncrypted { 16 | 17 | private static final String TAG = "DatabaseEncrypted"; 18 | 19 | /** 20 | * 将未加密数据库转换为加密数据库 21 | * 22 | * @param application application 23 | * @param unencryptedDbName 未加密数据库名 24 | * @param encryptedDbName 加密数据库名 25 | * @param password 加密数据库密码 26 | */ 27 | public static boolean importUnencryptedDatabase(Application application, String unencryptedDbName, String encryptedDbName, String password) { 28 | File unencryptedFile = application.getDatabasePath(unencryptedDbName); 29 | if (!unencryptedFile.exists()) { 30 | Log.i(TAG, "unencrypted database not exist"); 31 | return true; 32 | } 33 | 34 | File encryptedFile = application.getDatabasePath(encryptedDbName); 35 | if (encryptedFile.exists()) { 36 | Log.i(TAG, "encrypted database is exist"); 37 | return true; 38 | } 39 | 40 | Log.i(TAG, "encrypted database"); 41 | SQLiteDatabase database = null; 42 | try { 43 | database = SQLiteDatabase.openOrCreateDatabase(unencryptedFile, "", null); 44 | 45 | Log.i(TAG, "未加密的数据库数据"); 46 | Cursor cursor = database.rawQuery("select * from city", new String[]{}); 47 | while (cursor.moveToNext()) { 48 | Log.i(TAG, "cityName:" + cursor.getString(cursor.getColumnIndex("cityName"))); 49 | } 50 | cursor.close(); 51 | 52 | database.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s'", 53 | encryptedFile.getAbsolutePath(), password)); 54 | database.rawExecSQL("select sqlcipher_export('encrypted')"); 55 | database.rawExecSQL("DETACH DATABASE encrypted"); 56 | 57 | // 测试数据是否拷贝成功 58 | database = SQLiteDatabase.openOrCreateDatabase(encryptedFile, password, null); 59 | Log.i(TAG, "加密的数据库数据"); 60 | while (cursor.moveToNext()) { 61 | Log.i(TAG, "cityName:" + cursor.getString(cursor.getColumnIndex("cityName"))); 62 | } 63 | cursor.close(); 64 | 65 | return true; 66 | } catch (Exception e) { 67 | e.printStackTrace(); 68 | return false; 69 | } finally { 70 | if (database != null) { 71 | database.close(); 72 | } 73 | application.deleteDatabase(unencryptedDbName); 74 | SQLiteDatabase.releaseMemory(); 75 | } 76 | } 77 | 78 | /** 79 | * 将加密数据库转换为常规数据库 80 | * 81 | * @param application application 82 | * @param encryptedDbName 加密数据库名 83 | * @param unencryptedDbName 未加密数据库名 84 | * @param password 加密数据库密码 85 | */ 86 | public static boolean exportToUnencryptedDatabase(Application application, String encryptedDbName, String unencryptedDbName, String password) { 87 | File encryptedFile = application.getDatabasePath(encryptedDbName); 88 | if (!encryptedFile.exists()) { 89 | Log.i(TAG, "encrypted database not exist"); 90 | return true; 91 | } 92 | 93 | File unencryptedFile = application.getDatabasePath(unencryptedDbName); 94 | if (unencryptedFile.exists()) { 95 | Log.i(TAG, "unencrypted database is exist"); 96 | return true; 97 | } 98 | 99 | Log.i(TAG, "unencrypted database"); 100 | SQLiteDatabase database = null; 101 | try { 102 | database = SQLiteDatabase.openOrCreateDatabase(encryptedFile, password, null); 103 | 104 | Log.i(TAG, "加密的数据库数据"); 105 | Cursor cursor = database.rawQuery("select * from city", new String[]{}); 106 | while (cursor.moveToNext()) { 107 | Log.i(TAG, "cityName:" + cursor.getString(cursor.getColumnIndex("cityName"))); 108 | } 109 | cursor.close(); 110 | 111 | application.deleteDatabase(unencryptedDbName); 112 | database.rawExecSQL(String.format("ATTACH DATABASE '%s' as plaintext KEY '';", 113 | unencryptedFile.getAbsolutePath())); 114 | database.rawExecSQL("SELECT sqlcipher_export('plaintext');"); 115 | database.rawExecSQL("DETACH DATABASE plaintext;"); 116 | 117 | // 测试数据是否拷贝成功 118 | database = SQLiteDatabase.openOrCreateDatabase(unencryptedFile, "", null, null); 119 | Log.i(TAG, "解密的数据库数据"); 120 | cursor = database.rawQuery("select * from city", new String[]{}); 121 | while (cursor.moveToNext()) { 122 | Log.i(TAG, "cityName:" + cursor.getString(cursor.getColumnIndex("cityName"))); 123 | } 124 | 125 | cursor.close(); 126 | 127 | return true; 128 | } catch (Exception e) { 129 | e.printStackTrace(); 130 | return false; 131 | } finally { 132 | if (database != null) { 133 | database.close(); 134 | } 135 | application.deleteDatabase(encryptedDbName); 136 | SQLiteDatabase.releaseMemory(); 137 | } 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/huangzj/dbencrypt/dao/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.example.huangzj.dbencrypt.dao; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | 6 | import com.example.huangzj.dbencrypt.LogUtil; 7 | import com.example.huangzj.dbencrypt.dao.bean.City; 8 | import com.j256.ormlite.dao.Dao; 9 | import com.j256.ormlite.misc.JavaxPersistence; 10 | import com.j256.ormlite.sqlcipher.android.apptools.OrmLiteSqliteOpenHelper; 11 | import com.j256.ormlite.support.ConnectionSource; 12 | import com.j256.ormlite.table.DatabaseTable; 13 | import com.j256.ormlite.table.TableUtils; 14 | 15 | import net.sqlcipher.database.SQLiteDatabase; 16 | 17 | import java.sql.SQLException; 18 | import java.util.HashMap; 19 | import java.util.Iterator; 20 | import java.util.Map; 21 | 22 | /** 23 | * ormlite操作数据库Helper 24 | */ 25 | public class DatabaseHelper extends OrmLiteSqliteOpenHelper { 26 | 27 | /** 28 | * 数据库名称 29 | */ 30 | public static final String DATABASE_NAME = "unencrypted"; 31 | public static final String ENCRYPTED_DATABASE_NAME = "encrypted"; 32 | 33 | public static final String PASSWORD = "DB-way2-password"; 34 | 35 | /** 36 | * 数据库版本号 37 | */ 38 | private static final int DATABASE_VERSION = 1; 39 | 40 | private static DatabaseHelper instance; 41 | 42 | /** 43 | * dao缓存 44 | */ 45 | private Map daoMap; 46 | 47 | private DatabaseHelper(Context context) { 48 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 49 | daoMap = new HashMap<>(); 50 | } 51 | 52 | public static DatabaseHelper getInstance(Context context) { 53 | if (instance == null) { 54 | synchronized (DatabaseHelper.class) { 55 | if (instance == null) { 56 | instance = new DatabaseHelper(context); 57 | } 58 | } 59 | } 60 | return instance; 61 | } 62 | 63 | @Override 64 | public void onCreate(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource) { 65 | createTables(sqLiteDatabase, connectionSource); 66 | } 67 | 68 | /** 69 | * 数据库升级,注意控制好数据库版本号,不然此方法将不会被调用到 70 | */ 71 | @Override 72 | public void onUpgrade(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource, int oldVersion, int newVersion) { 73 | dropTables(connectionSource); 74 | onCreate(sqLiteDatabase, connectionSource); 75 | } 76 | 77 | private void createTables(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource) { 78 | if (isTableExist(sqLiteDatabase, City.class)) { 79 | LogUtil.d("数据表已经存在了"); 80 | return; 81 | } 82 | try { 83 | TableUtils.createTable(connectionSource, City.class); 84 | } catch (SQLException e) { 85 | LogUtil.e(e); 86 | } 87 | } 88 | 89 | private void dropTables(ConnectionSource connectionSource) { 90 | try { 91 | TableUtils.dropTable(connectionSource, City.class, true); 92 | } catch (SQLException e) { 93 | LogUtil.e(e); 94 | } 95 | } 96 | 97 | public synchronized Dao getDao(Class cls) { 98 | Dao dao; 99 | String clsName = cls.getSimpleName(); 100 | if (daoMap.containsKey(clsName)) { 101 | dao = daoMap.get(clsName); 102 | } else { 103 | try { 104 | dao = super.getDao(cls); 105 | } catch (SQLException e) { 106 | LogUtil.e(e); 107 | return null; 108 | } 109 | daoMap.put(clsName, dao); 110 | } 111 | return dao; 112 | } 113 | 114 | @Override 115 | public void close() { 116 | super.close(); 117 | synchronized (this) { 118 | Iterator> it = daoMap.entrySet().iterator(); 119 | while (it.hasNext()) { 120 | Map.Entry entry = it.next(); 121 | Dao dao = entry.getValue(); 122 | dao = null; 123 | it.remove(); 124 | } 125 | } 126 | } 127 | 128 | private boolean isTableExist(SQLiteDatabase database, Class clazz) { 129 | boolean isTableExist = false; 130 | String tableName = extractTableName(clazz); 131 | Cursor cursor = database.rawQuery("select * from sqlite_master where type = ? AND name = ?", 132 | new String[]{"table", tableName}); 133 | if (cursor != null) { 134 | cursor.moveToFirst(); 135 | int columnIndex = cursor.getColumnIndex("sql"); 136 | if (-1 != columnIndex && cursor.getCount() > 0) { 137 | isTableExist = true; 138 | } 139 | cursor.close(); 140 | } 141 | return isTableExist; 142 | } 143 | 144 | /** 145 | * 获取表名 146 | * 147 | * @param clazz 148 | * @param 149 | * @return 150 | */ 151 | public static String extractTableName(Class clazz) { 152 | DatabaseTable databaseTable = clazz.getAnnotation(DatabaseTable.class); 153 | String name; 154 | if (databaseTable != null && databaseTable.tableName() != null && databaseTable.tableName().length() > 0) { 155 | name = databaseTable.tableName(); 156 | } else { 157 | name = JavaxPersistence.getEntityName(clazz); 158 | if (name == null) { 159 | name = clazz.getSimpleName().toLowerCase(); 160 | } 161 | } 162 | return name; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/huangzj/dbencrypt/dao/bean/City.java: -------------------------------------------------------------------------------- 1 | package com.example.huangzj.dbencrypt.dao.bean; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | @DatabaseTable(tableName = "city") 7 | public class City { 8 | 9 | @DatabaseField(generatedId = true) 10 | private Integer id; 11 | 12 | @DatabaseField 13 | private String cityNo; 14 | 15 | @DatabaseField 16 | private String cityName; 17 | 18 | @DatabaseField 19 | private String provinceNo; 20 | 21 | public Integer getId() { 22 | return id; 23 | } 24 | 25 | public void setId(Integer id) { 26 | this.id = id; 27 | } 28 | 29 | public String getCityNo() { 30 | return cityNo; 31 | } 32 | 33 | public void setCityNo(String cityNo) { 34 | this.cityNo = cityNo; 35 | } 36 | 37 | public String getCityName() { 38 | return cityName; 39 | } 40 | 41 | public void setCityName(String cityName) { 42 | this.cityName = cityName; 43 | } 44 | 45 | public String getProvinceNo() { 46 | return provinceNo; 47 | } 48 | 49 | public void setProvinceNo(String provinceNo) { 50 | this.provinceNo = provinceNo; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "City{" + 56 | "id=" + id + 57 | ", cityNo='" + cityNo + '\'' + 58 | ", cityName='" + cityName + '\'' + 59 | ", provinceNo='" + provinceNo + '\'' + 60 | '}'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/huangzj/dbencrypt/dao/bean/CityDao.java: -------------------------------------------------------------------------------- 1 | package com.example.huangzj.dbencrypt.dao.bean; 2 | 3 | import android.content.Context; 4 | 5 | import com.example.huangzj.dbencrypt.dao.ormlite.OrmLiteDao; 6 | 7 | import java.util.List; 8 | 9 | 10 | public class CityDao extends OrmLiteDao { 11 | 12 | public CityDao(Context context) { 13 | super(context, City.class); 14 | } 15 | 16 | public boolean createForBatch(List list) { 17 | return super.insertForBatch(list); 18 | } 19 | 20 | public List queryByProvinceNo(String provinceNo) { 21 | return super.queryByColumnName("provinceNo", provinceNo); 22 | } 23 | 24 | public City queryByCityNo(String cityNo) { 25 | return super.queryForFirst("cityNo", cityNo); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/huangzj/dbencrypt/dao/ormlite/DaoListener.java: -------------------------------------------------------------------------------- 1 | package com.example.huangzj.dbencrypt.dao.ormlite; 2 | 3 | 4 | public class DaoListener { 5 | 6 | private DaoThreadMode daoThreadMode = DaoThreadMode.MainThread; 7 | 8 | public void onDataChanged(int daoOperationType, Object data){} 9 | 10 | public void onDataChanged(Object data){} 11 | 12 | protected DaoThreadMode getDaoThreadMode() { 13 | return daoThreadMode; 14 | } 15 | 16 | protected void setDaoThreadMode(DaoThreadMode daoThreadMode) { 17 | this.daoThreadMode = daoThreadMode; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/huangzj/dbencrypt/dao/ormlite/DaoObserver.java: -------------------------------------------------------------------------------- 1 | package com.example.huangzj.dbencrypt.dao.ormlite; 2 | 3 | import android.os.Handler; 4 | import android.os.HandlerThread; 5 | import android.os.Looper; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | 11 | public class DaoObserver { 12 | 13 | private static List listeners = new ArrayList<>(); 14 | 15 | private static Handler uiHandler = new Handler(Looper.getMainLooper()); 16 | 17 | private static Handler asyncHandler; 18 | 19 | private static HandlerThread handlerThread; 20 | 21 | static { 22 | handlerThread = new HandlerThread("backgroud_thread"); 23 | handlerThread.start(); 24 | asyncHandler = new Handler(handlerThread.getLooper()); 25 | } 26 | 27 | public static void regist(DaoListener daoListener) { 28 | daoListener.setDaoThreadMode(DaoThreadMode.MainThread); 29 | regist(daoListener, DaoThreadMode.MainThread); 30 | } 31 | 32 | public static void regist(DaoListener daoListener, DaoThreadMode daoThreadMode) { 33 | daoListener.setDaoThreadMode(daoThreadMode); 34 | listeners.add(daoListener); 35 | } 36 | 37 | public static void unRegist(DaoListener daoListener) { 38 | listeners.remove(daoListener); 39 | } 40 | 41 | /** 42 | * 回调将保证在主线程中执行,禁止执行耗时任务,若执行耗时任务则要另开线程 43 | * 44 | * @param data 45 | */ 46 | public static synchronized void publish(int daoOperationType, Object data) { 47 | notifyDataChanged(daoOperationType, data); 48 | } 49 | 50 | private static void notifyDataChanged(final int daoOperationType, final Object data) { 51 | for (final DaoListener daoListener : listeners) { 52 | DaoThreadMode daoThreadMode = daoListener.getDaoThreadMode(); 53 | if (daoThreadMode == DaoThreadMode.MainThread) { 54 | uiHandler.post(new Runnable() { 55 | @Override 56 | public void run() { 57 | onDataChanged(daoListener, daoOperationType, data); 58 | } 59 | }); 60 | } else if (daoThreadMode == DaoThreadMode.BackgroundThread) { 61 | if (Looper.myLooper() == Looper.getMainLooper()) { 62 | asyncHandler.post(new Runnable() { 63 | @Override 64 | public void run() { 65 | onDataChanged(daoListener, daoOperationType, data); 66 | } 67 | }); 68 | } else { 69 | onDataChanged(daoListener, daoOperationType, data); 70 | } 71 | } else if (daoThreadMode == DaoThreadMode.Async) { 72 | asyncHandler.post(new Runnable() { 73 | @Override 74 | public void run() { 75 | onDataChanged(daoListener, daoOperationType, data); 76 | } 77 | }); 78 | } else { 79 | // PostThread线程模式 80 | onDataChanged(daoListener, daoOperationType, data); 81 | } 82 | } 83 | } 84 | 85 | private static void onDataChanged(DaoListener daoListener, int daoOperationType, Object data) { 86 | daoListener.onDataChanged(daoOperationType, data); 87 | daoListener.onDataChanged(data); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/huangzj/dbencrypt/dao/ormlite/DaoOperation.java: -------------------------------------------------------------------------------- 1 | package com.example.huangzj.dbencrypt.dao.ormlite; 2 | 3 | 4 | public interface DaoOperation { 5 | 6 | int INSERT = 1; 7 | int DELETE = 2; 8 | int UPDATE = 3; 9 | int SELECT = 4; 10 | int INSERT_BATCH = 5; 11 | int DELETE_BATCH = 6; 12 | int UPDATE_BATCH = 7; 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/huangzj/dbencrypt/dao/ormlite/DaoThreadMode.java: -------------------------------------------------------------------------------- 1 | package com.example.huangzj.dbencrypt.dao.ormlite; 2 | 3 | 4 | public enum DaoThreadMode { 5 | 6 | /** 7 | * 同一个线程 8 | */ 9 | PostThread, 10 | 11 | /** 12 | * 主线程 13 | */ 14 | MainThread, 15 | 16 | /** 17 | * 后台线程 18 | */ 19 | BackgroundThread, 20 | 21 | /** 22 | * 单独开一个线程 23 | */ 24 | Async 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | 14 | 18 | 19 | 23 | 24 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000000 4 | #fa6666 5 | #ffffff 6 | #ff9933 7 | #ff6000 8 | #fdbb2e 9 | #ffcc92 10 | #f67f09 11 | #ff8800 12 | #bbbbbb 13 | #32bf99 14 | 15 | #ff6000 16 | 17 | #c2c1c1 18 | 19 | #ebebeb 20 | #ffffff 21 | #00ffffff 22 | 23 | #222222 24 | #525252 25 | #222222 26 | #333333 27 | #725640 28 | #888888 29 | #7f7f7f 30 | #f2ebe1 31 | #e1e1e1 32 | #dadbdc 33 | 34 | #555555 35 | 36 | #b9a580 37 | 38 | #cccccc 39 | 40 | #f0f0f0 41 | #a0a0a0 42 | #6dddbd 43 | 44 | 45 | #888888 46 | #fa6666 47 | #222222 48 | #888888 49 | 50 | #99000000 51 | #78000000 52 | #6633B5E5 53 | #f6f5f5 54 | #6dddbd 55 | #ff9933 56 | #fa6666 57 | #ffffff 58 | 59 | #f4f6f6 60 | 61 | 62 | #7fd8c0 63 | #f7c544 64 | #8bcbea 65 | #969ff3 66 | #f78080 67 | #dc83cc 68 | #00ffa2 69 | #c2c1c1 70 | #ff6a0e 71 | #e3e3e3 72 | #999999 73 | #ff9933 74 | #7ff2f2f2 75 | 76 | #888888 77 | #555555 78 | #32d787 79 | #555555 80 | #ff4155 81 | 82 | #C0C0C0 83 | #343434 84 | 85 | 86 | #ebedf0 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | DbEncrypt 3 | 4 | Hello world! 5 | Settings 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | maven { url "http://172.28.10.222:8081/nexus/content/groups/android_public/" } 7 | 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:2.2.2' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | jcenter() 20 | maven { url "http://172.28.10.222:8081/nexus/content/groups/android_public/" } 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } 27 | -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | //编译SDK版本 3 | compileSdkVer=23 4 | //构建工具版本 5 | buildToolsVer='23.0.3' 6 | //最低兼容版本 7 | minSdkVer=14 8 | //测试过的版本 9 | targetSdkVer=23 10 | //支持库的版本 11 | supportVer='23.3.0' 12 | //JDK版本 13 | sourceCompatVer=JavaVersion.VERSION_1_7 14 | targetCompatVer=JavaVersion.VERSION_1_7 15 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 12 19:50:59 CST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /maven_user.properties: -------------------------------------------------------------------------------- 1 | repository.user=android 2 | repository.password=android123 3 | #repository.url=http://172.28.10.222:8081/nexus/content/repositories/android-release/ 4 | repository.url=http://172.28.10.222:8081/nexus/content/repositories/android-snapshots/ 5 | -------------------------------------------------------------------------------- /ormlite-sqlcipher.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':sqlcipher' 3 | //include ':sqlcipher-old' -------------------------------------------------------------------------------- /sqlcipher-old/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sqlcipher-old/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply from: 'maven_upload.gradle' 3 | apply from: '../config.gradle' 4 | 5 | android { 6 | compileSdkVersion compileSdkVer 7 | buildToolsVersion buildToolsVer 8 | 9 | defaultConfig { 10 | minSdkVersion minSdkVer 11 | targetSdkVersion targetSdkVer 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | 22 | compileOptions { 23 | sourceCompatibility sourceCompatVer 24 | targetCompatibility targetCompatVer 25 | } 26 | 27 | sourceSets { 28 | main { 29 | jniLibs.srcDirs = ['libs'] 30 | } 31 | } 32 | } 33 | 34 | //repositories { 35 | // flatDir { 36 | // dirs 'libs' //this way we can find the .aar file in libs folder 37 | // } 38 | //} 39 | 40 | dependencies { 41 | compile fileTree(dir: 'libs', include: ['*.jar']) 42 | compile 'com.j256.ormlite:ormlite-core:4.48' 43 | compile 'com.j256.ormlite:ormlite-android:4.48' 44 | } 45 | -------------------------------------------------------------------------------- /sqlcipher-old/libs/armeabi/libdatabase_sqlcipher.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/sqlcipher-old/libs/armeabi/libdatabase_sqlcipher.so -------------------------------------------------------------------------------- /sqlcipher-old/libs/armeabi/libsqlcipher_android.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/sqlcipher-old/libs/armeabi/libsqlcipher_android.so -------------------------------------------------------------------------------- /sqlcipher-old/libs/armeabi/libstlport_shared.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/sqlcipher-old/libs/armeabi/libstlport_shared.so -------------------------------------------------------------------------------- /sqlcipher-old/libs/guava-r09.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/sqlcipher-old/libs/guava-r09.jar -------------------------------------------------------------------------------- /sqlcipher-old/libs/sqlcipher.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/sqlcipher-old/libs/sqlcipher.jar -------------------------------------------------------------------------------- /sqlcipher-old/maven_pom.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=android 2 | POM_DESCRIPTION=app sql cipher lib 3 | POM_GROUP=com.xtc.data 4 | POM_ARTIFACT_ID=data-sqlcipher 5 | POM_PACKAGING=aar 6 | POM_VERSION=0.0.1-SNAPSHOT -------------------------------------------------------------------------------- /sqlcipher-old/maven_upload.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven' 2 | 3 | Properties user_properties = new Properties() 4 | user_properties.load(project.rootProject.file('maven_user.properties').newDataInputStream()) 5 | def repoUrl = user_properties.getProperty("repository.url") 6 | def userName = user_properties.getProperty("repository.user") 7 | def userPassword = user_properties.getProperty("repository.password") 8 | 9 | Properties pom_properties = new Properties() 10 | pom_properties.load(project.file('maven_pom.properties').newDataInputStream()) 11 | def pom_name = pom_properties.getProperty("POM_NAME") 12 | def pom_description = pom_properties.getProperty("POM_DESCRIPTION") 13 | def pom_group = pom_properties.getProperty("POM_GROUP") 14 | def pom_artifact_id = pom_properties.getProperty("POM_ARTIFACT_ID") 15 | def pom_packaging = pom_properties.getProperty("POM_PACKAGING") 16 | def pom_version = pom_properties.getProperty("POM_VERSION") 17 | 18 | 19 | uploadArchives { 20 | 21 | repositories.mavenDeployer { 22 | repository(url: repoUrl) { 23 | authentication(userName: userName, 24 | password: userPassword) 25 | } 26 | 27 | pom.project { 28 | name pom_name 29 | description pom_description 30 | groupId pom_group 31 | artifactId pom_artifact_id 32 | version pom_version 33 | packaging pom_packaging 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /sqlcipher-old/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Stay/develop/android-sdk-mac_x86/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -keep class net.sqlcipher.** {*;} 19 | -keep class net.sqlcipher.database.** {*;} -------------------------------------------------------------------------------- /sqlcipher-old/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/assets/icudt46l.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wobuaihuangjun/ormlite-sqlcipher/28dca4578e1dfe2785dcf1184c4125d8d2ffd438/sqlcipher-old/src/main/assets/icudt46l.zip -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/AndroidCompiledStatement.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android; 2 | 3 | 4 | import com.j256.ormlite.dao.ObjectCache; 5 | import com.j256.ormlite.field.SqlType; 6 | import com.j256.ormlite.logger.Logger; 7 | import com.j256.ormlite.logger.LoggerFactory; 8 | import com.j256.ormlite.misc.SqlExceptionUtil; 9 | import com.j256.ormlite.sqlcipher.android.compat.ApiCompatibility; 10 | import com.j256.ormlite.sqlcipher.android.compat.ApiCompatibilityUtils; 11 | import com.j256.ormlite.stmt.StatementBuilder.StatementType; 12 | import com.j256.ormlite.support.CompiledStatement; 13 | import com.j256.ormlite.support.DatabaseResults; 14 | 15 | import net.sqlcipher.Cursor; 16 | import net.sqlcipher.database.SQLiteDatabase; 17 | import net.sqlcipher.database.SQLiteStatement; 18 | 19 | import java.sql.SQLException; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * Android implementation of the compiled statement. 25 | * 26 | * @author kevingalligan, graywatson 27 | */ 28 | public class AndroidCompiledStatement implements CompiledStatement { 29 | 30 | private static Logger logger = LoggerFactory.getLogger(AndroidCompiledStatement.class); 31 | 32 | private static final String[] NO_STRING_ARGS = new String[0]; 33 | private static final ApiCompatibility apiCompatibility = ApiCompatibilityUtils.getCompatibility(); 34 | 35 | private final String sql; 36 | private final SQLiteDatabase db; 37 | private final StatementType type; 38 | private final boolean cancelQueriesEnabled; 39 | 40 | private Cursor cursor; 41 | private List args; 42 | private Integer max; 43 | private ApiCompatibility.CancellationHook cancellationHook; 44 | 45 | public AndroidCompiledStatement(String sql, SQLiteDatabase db, StatementType type, boolean cancelQueriesEnabled) { 46 | this.sql = sql; 47 | this.db = db; 48 | this.type = type; 49 | this.cancelQueriesEnabled = cancelQueriesEnabled; 50 | } 51 | 52 | public int getColumnCount() throws SQLException { 53 | return getCursor().getColumnCount(); 54 | } 55 | 56 | public String getColumnName(int column) throws SQLException { 57 | return getCursor().getColumnName(column); 58 | } 59 | 60 | public DatabaseResults runQuery(ObjectCache objectCache) throws SQLException { 61 | // this could come from DELETE or UPDATE, just not a SELECT 62 | if (!type.isOkForQuery()) { 63 | throw new IllegalArgumentException("Cannot call query on a " + type + " statement"); 64 | } 65 | return new AndroidDatabaseResults(getCursor(), objectCache); 66 | } 67 | 68 | public int runUpdate() throws SQLException { 69 | if (!type.isOkForUpdate()) { 70 | throw new IllegalArgumentException("Cannot call update on a " + type + " statement"); 71 | } 72 | String finalSql; 73 | if (max == null) { 74 | finalSql = sql; 75 | } else { 76 | finalSql = sql + " " + max; 77 | } 78 | return execSql(db, "runUpdate", finalSql, getArgArray()); 79 | } 80 | 81 | public int runExecute() throws SQLException { 82 | if (!type.isOkForExecute()) { 83 | throw new IllegalArgumentException("Cannot call execute on a " + type + " statement"); 84 | } 85 | return execSql(db, "runExecute", sql, getArgArray()); 86 | } 87 | 88 | public void close() throws SQLException { 89 | if (cursor != null) { 90 | try { 91 | cursor.close(); 92 | } catch (android.database.SQLException e) { 93 | throw SqlExceptionUtil.create("Problems closing Android cursor", e); 94 | } 95 | } 96 | cancellationHook = null; 97 | } 98 | 99 | public void closeQuietly() { 100 | try { 101 | close(); 102 | } catch (SQLException e) { 103 | // ignored 104 | } 105 | } 106 | 107 | public void cancel() { 108 | if (cancellationHook != null) { 109 | cancellationHook.cancel(); 110 | } 111 | } 112 | 113 | public void setObject(int parameterIndex, Object obj, SqlType sqlType) throws SQLException { 114 | isInPrep(); 115 | if (args == null) { 116 | args = new ArrayList(); 117 | } 118 | if (obj == null) { 119 | args.add(parameterIndex, null); 120 | return; 121 | } 122 | 123 | switch (sqlType) { 124 | case STRING : 125 | case LONG_STRING : 126 | case DATE : 127 | case BOOLEAN : 128 | case CHAR : 129 | case BYTE : 130 | case SHORT : 131 | case INTEGER : 132 | case LONG : 133 | case FLOAT : 134 | case DOUBLE : 135 | args.add(parameterIndex, obj.toString()); 136 | break; 137 | case BYTE_ARRAY : 138 | case SERIALIZABLE : 139 | args.add(parameterIndex, obj); 140 | break; 141 | case BLOB : 142 | // this is only for derby serializable 143 | case BIG_DECIMAL : 144 | // this should be handled as a STRING 145 | throw new SQLException("Invalid Android type: " + sqlType); 146 | case UNKNOWN : 147 | default : 148 | throw new SQLException("Unknown sql argument type: " + sqlType); 149 | } 150 | } 151 | 152 | public void setMaxRows(int max) throws SQLException { 153 | isInPrep(); 154 | this.max = max; 155 | } 156 | 157 | public void setQueryTimeout(long millis) { 158 | // as far as I could tell this is not supported by Android API 159 | } 160 | 161 | /*** 162 | * This is mostly an internal class but is exposed for those people who need access to the Cursor itself. 163 | * 164 | *

165 | * NOTE: This is not thread safe. Not sure if we need it, but keep that in mind. 166 | *

167 | */ 168 | public Cursor getCursor() throws SQLException { 169 | if (cursor == null) { 170 | String finalSql = null; 171 | try { 172 | if (max == null) { 173 | finalSql = sql; 174 | } else { 175 | finalSql = sql + " " + max; 176 | } 177 | if (cancelQueriesEnabled) { 178 | cancellationHook = apiCompatibility.createCancellationHook(); 179 | } 180 | cursor = apiCompatibility.rawQuery(db, finalSql, getStringArray(), cancellationHook); 181 | cursor.moveToFirst(); 182 | logger.trace("{}: started rawQuery cursor for: {}", this, finalSql); 183 | } catch (android.database.SQLException e) { 184 | throw SqlExceptionUtil.create("Problems executing Android query: " + finalSql, e); 185 | } 186 | } 187 | 188 | return cursor; 189 | } 190 | 191 | @Override 192 | public String toString() { 193 | return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode()); 194 | } 195 | 196 | /** 197 | * Execute some SQL on the database and return the number of rows changed. 198 | */ 199 | static int execSql(SQLiteDatabase db, String label, String finalSql, Object[] argArray) throws SQLException { 200 | try { 201 | db.execSQL(finalSql, argArray); 202 | } catch (android.database.SQLException e) { 203 | throw SqlExceptionUtil.create("Problems executing " + label + " Android statement: " + finalSql, e); 204 | } 205 | int result; 206 | SQLiteStatement stmt = null; 207 | try { 208 | // ask sqlite how many rows were just changed 209 | stmt = db.compileStatement("SELECT CHANGES()"); 210 | result = (int) stmt.simpleQueryForLong(); 211 | } catch (android.database.SQLException e) { 212 | // ignore the exception and just return 1 if it failed 213 | result = 1; 214 | } finally { 215 | if (stmt != null) { 216 | stmt.close(); 217 | } 218 | } 219 | logger.trace("executing statement {} changed {} rows: {}", label, result, finalSql); 220 | return result; 221 | } 222 | 223 | private void isInPrep() throws SQLException { 224 | if (cursor != null) { 225 | throw new SQLException("Query already run. Cannot add argument values."); 226 | } 227 | } 228 | 229 | private Object[] getArgArray() { 230 | if (args == null) { 231 | // this will work for Object[] as well as String[] 232 | return NO_STRING_ARGS; 233 | } else { 234 | return args.toArray(new Object[args.size()]); 235 | } 236 | } 237 | 238 | private String[] getStringArray() { 239 | if (args == null) { 240 | return NO_STRING_ARGS; 241 | } else { 242 | // we assume we have Strings in args 243 | return args.toArray(new String[args.size()]); 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/AndroidConnectionSource.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android; 2 | 3 | 4 | import com.j256.ormlite.db.DatabaseType; 5 | import com.j256.ormlite.logger.Logger; 6 | import com.j256.ormlite.logger.LoggerFactory; 7 | import com.j256.ormlite.misc.SqlExceptionUtil; 8 | import com.j256.ormlite.sqlcipher.android.apptools.OrmLiteSqliteOpenHelper; 9 | import com.j256.ormlite.sqlcipher.db.SqliteAndroidDatabaseType; 10 | import com.j256.ormlite.support.BaseConnectionSource; 11 | import com.j256.ormlite.support.ConnectionSource; 12 | import com.j256.ormlite.support.DatabaseConnection; 13 | import com.j256.ormlite.support.DatabaseConnectionProxyFactory; 14 | 15 | import net.sqlcipher.database.SQLiteDatabase; 16 | import net.sqlcipher.database.SQLiteOpenHelper; 17 | 18 | import java.sql.SQLException; 19 | 20 | /** 21 | * Android version of the connection source. Takes a standard Android {@link SQLiteOpenHelper}. For best results, use 22 | * {@link OrmLiteSqliteOpenHelper}. You can also construct with a {@link SQLiteDatabase}. 23 | * 24 | * @author kevingalligan, graywatson 25 | */ 26 | public class AndroidConnectionSource extends BaseConnectionSource implements ConnectionSource { 27 | 28 | private static final Logger logger = LoggerFactory.getLogger(AndroidConnectionSource.class); 29 | 30 | private final SQLiteOpenHelper helper; 31 | private final SQLiteDatabase sqliteDatabase; 32 | private DatabaseConnection connection = null; 33 | private volatile boolean isOpen = true; 34 | private final DatabaseType databaseType = new SqliteAndroidDatabaseType(); 35 | private static DatabaseConnectionProxyFactory connectionProxyFactory; 36 | private boolean cancelQueriesEnabled = false; 37 | 38 | public AndroidConnectionSource(SQLiteOpenHelper helper) { 39 | this.helper = helper; 40 | this.sqliteDatabase = null; 41 | } 42 | 43 | public AndroidConnectionSource(SQLiteDatabase sqliteDatabase) { 44 | this.helper = null; 45 | this.sqliteDatabase = sqliteDatabase; 46 | } 47 | 48 | public DatabaseConnection getReadOnlyConnection() throws SQLException { 49 | /* 50 | * We have to use the read-write connection because getWritableDatabase() can call close on 51 | * getReadableDatabase() in the future. This has something to do with Android's SQLite connection management. 52 | * 53 | * See android docs: http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html 54 | */ 55 | return getReadWriteConnection(); 56 | } 57 | 58 | public DatabaseConnection getReadWriteConnection() throws SQLException { 59 | DatabaseConnection conn = getSavedConnection(); 60 | if (conn != null) { 61 | return conn; 62 | } 63 | if (connection == null) { 64 | SQLiteDatabase db; 65 | if (sqliteDatabase == null) { 66 | String password ; 67 | if (helper instanceof OrmLiteSqliteOpenHelper){ 68 | password = ((OrmLiteSqliteOpenHelper)helper).getCipherPassword(); 69 | }else { 70 | throw new IllegalArgumentException("you should set sqlcipher password to open database"); 71 | } 72 | try { 73 | db = helper.getWritableDatabase(password); 74 | } catch (android.database.SQLException e) { 75 | throw SqlExceptionUtil.create("Getting a writable database from helper " + helper + " failed", e); 76 | } 77 | } else { 78 | db = sqliteDatabase; 79 | } 80 | connection = new AndroidDatabaseConnection(db, true, cancelQueriesEnabled); 81 | if (connectionProxyFactory != null) { 82 | connection = connectionProxyFactory.createProxy(connection); 83 | } 84 | logger.trace("created connection {} for db {}, helper {}", connection, db, helper); 85 | } else { 86 | logger.trace("{}: returning read-write connection {}, helper {}", this, connection, helper); 87 | } 88 | return connection; 89 | } 90 | 91 | public void releaseConnection(DatabaseConnection connection) { 92 | // noop since connection management is handled by AndroidOS 93 | } 94 | 95 | public boolean saveSpecialConnection(DatabaseConnection connection) throws SQLException { 96 | return saveSpecial(connection); 97 | } 98 | 99 | public void clearSpecialConnection(DatabaseConnection connection) { 100 | clearSpecial(connection, logger); 101 | } 102 | 103 | public void close() { 104 | // the helper is closed so it calls close here, so this CANNOT be a call back to helper.close() 105 | isOpen = false; 106 | } 107 | 108 | public void closeQuietly() { 109 | close(); 110 | } 111 | 112 | public DatabaseType getDatabaseType() { 113 | return databaseType; 114 | } 115 | 116 | public boolean isOpen() { 117 | return isOpen; 118 | } 119 | 120 | /** 121 | * Set to enable connection proxying. Set to null to disable. 122 | */ 123 | public static void setDatabaseConnectionProxyFactory(DatabaseConnectionProxyFactory connectionProxyFactory) { 124 | AndroidConnectionSource.connectionProxyFactory = connectionProxyFactory; 125 | } 126 | 127 | public boolean isCancelQueriesEnabled() { 128 | return cancelQueriesEnabled; 129 | } 130 | 131 | /** 132 | * Set to true to enable the canceling of queries. 133 | * 134 | *

135 | * NOTE: This will incur a slight memory increase for all Cursor based queries -- even if cancel is not 136 | * called for them. 137 | *

138 | */ 139 | public void setCancelQueriesEnabled(boolean cancelQueriesEnabled) { 140 | this.cancelQueriesEnabled = cancelQueriesEnabled; 141 | } 142 | 143 | @Override 144 | public String toString() { 145 | return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode()); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/AndroidDatabaseResults.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android; 2 | 3 | 4 | import com.j256.ormlite.dao.ObjectCache; 5 | import com.j256.ormlite.db.DatabaseType; 6 | import com.j256.ormlite.sqlcipher.db.SqliteAndroidDatabaseType; 7 | import com.j256.ormlite.support.DatabaseResults; 8 | 9 | import net.sqlcipher.Cursor; 10 | 11 | import java.io.ByteArrayInputStream; 12 | import java.io.InputStream; 13 | import java.math.BigDecimal; 14 | import java.sql.SQLException; 15 | import java.sql.Timestamp; 16 | import java.util.Arrays; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | /** 21 | * Android implementation of our results object. 22 | * 23 | * @author kevingalligan, graywatson 24 | */ 25 | public class AndroidDatabaseResults implements DatabaseResults { 26 | 27 | private static final int MIN_NUM_COLUMN_NAMES_MAP = 8; 28 | 29 | private final Cursor cursor; 30 | private final String[] columnNames; 31 | private final Map columnNameMap; 32 | private final ObjectCache objectCache; 33 | private static final DatabaseType databaseType = new SqliteAndroidDatabaseType(); 34 | 35 | public AndroidDatabaseResults(Cursor cursor, ObjectCache objectCache) { 36 | this.cursor = cursor; 37 | this.columnNames = cursor.getColumnNames(); 38 | if (this.columnNames.length >= MIN_NUM_COLUMN_NAMES_MAP) { 39 | this.columnNameMap = new HashMap(); 40 | for (int i = 0; i < this.columnNames.length; i++) { 41 | // NOTE: this is case sensitive 42 | this.columnNameMap.put(this.columnNames[i], i); 43 | } 44 | } else { 45 | columnNameMap = null; 46 | } 47 | this.objectCache = objectCache; 48 | } 49 | 50 | /** 51 | * Constructor that allows you to inject a cursor that has already been configured with first-call set to false. 52 | * 53 | * @deprecated The firstCall is no longer needed since the library now calls first() and next on its own. 54 | */ 55 | @Deprecated 56 | public AndroidDatabaseResults(Cursor cursor, boolean firstCall, ObjectCache objectCache) { 57 | this(cursor, objectCache); 58 | } 59 | 60 | public int getColumnCount() { 61 | return cursor.getColumnCount(); 62 | } 63 | 64 | public String[] getColumnNames() { 65 | int colN = getColumnCount(); 66 | String[] columnNames = new String[colN]; 67 | for (int colC = 0; colC < colN; colC++) { 68 | columnNames[colC] = cursor.getColumnName(colC); 69 | } 70 | return columnNames; 71 | } 72 | 73 | public boolean first() { 74 | return cursor.moveToFirst(); 75 | } 76 | 77 | public boolean next() { 78 | return cursor.moveToNext(); 79 | } 80 | 81 | public boolean last() { 82 | return cursor.moveToLast(); 83 | } 84 | 85 | public boolean previous() { 86 | return cursor.moveToPrevious(); 87 | } 88 | 89 | public boolean moveRelative(int offset) { 90 | return cursor.move(offset); 91 | } 92 | 93 | public boolean moveAbsolute(int position) { 94 | return cursor.moveToPosition(position); 95 | } 96 | 97 | /** 98 | * Returns the count of results from the cursor. 99 | */ 100 | public int getCount() { 101 | return cursor.getCount(); 102 | } 103 | 104 | /** 105 | * Returns the position of the cursor in the list of results. 106 | */ 107 | public int getPosition() { 108 | return cursor.getPosition(); 109 | } 110 | 111 | public int findColumn(String columnName) throws SQLException { 112 | int index = lookupColumn(columnName); 113 | if (index >= 0) { 114 | return index; 115 | } 116 | 117 | /* 118 | * Hack here. It turns out that if we've asked for '*' then the field foo is in the cursor as foo. But if we ask 119 | * for a particular field list with DISTINCT, which escapes the field names, they are in the cursor _with_ the 120 | * escaping. Ugly!! 121 | */ 122 | StringBuilder sb = new StringBuilder(columnName.length() + 4); 123 | databaseType.appendEscapedEntityName(sb, columnName); 124 | index = lookupColumn(sb.toString()); 125 | if (index >= 0) { 126 | return index; 127 | } else { 128 | String[] columnNames = cursor.getColumnNames(); 129 | throw new SQLException("Unknown field '" + columnName + "' from the Android sqlite cursor, not in:" 130 | + Arrays.toString(columnNames)); 131 | } 132 | } 133 | 134 | public String getString(int columnIndex) { 135 | return cursor.getString(columnIndex); 136 | } 137 | 138 | public boolean getBoolean(int columnIndex) { 139 | if (cursor.isNull(columnIndex) || cursor.getShort(columnIndex) == 0) { 140 | return false; 141 | } else { 142 | return true; 143 | } 144 | } 145 | 146 | public char getChar(int columnIndex) throws SQLException { 147 | String string = cursor.getString(columnIndex); 148 | if (string == null || string.length() == 0) { 149 | return 0; 150 | } else if (string.length() == 1) { 151 | return string.charAt(0); 152 | } else { 153 | throw new SQLException("More than 1 character stored in database column: " + columnIndex); 154 | } 155 | } 156 | 157 | public byte getByte(int columnIndex) { 158 | return (byte) getShort(columnIndex); 159 | } 160 | 161 | public byte[] getBytes(int columnIndex) { 162 | return cursor.getBlob(columnIndex); 163 | } 164 | 165 | public short getShort(int columnIndex) { 166 | return cursor.getShort(columnIndex); 167 | } 168 | 169 | public int getInt(int columnIndex) { 170 | return cursor.getInt(columnIndex); 171 | } 172 | 173 | public long getLong(int columnIndex) { 174 | return cursor.getLong(columnIndex); 175 | } 176 | 177 | public float getFloat(int columnIndex) { 178 | return cursor.getFloat(columnIndex); 179 | } 180 | 181 | public double getDouble(int columnIndex) { 182 | return cursor.getDouble(columnIndex); 183 | } 184 | 185 | public Timestamp getTimestamp(int columnIndex) throws SQLException { 186 | throw new SQLException("Android does not support timestamp. Use JAVA_DATE_LONG or JAVA_DATE_STRING types"); 187 | } 188 | 189 | public InputStream getBlobStream(int columnIndex) { 190 | return new ByteArrayInputStream(cursor.getBlob(columnIndex)); 191 | } 192 | 193 | public BigDecimal getBigDecimal(int columnIndex) throws SQLException { 194 | throw new SQLException("Android does not support BigDecimal type. Use BIG_DECIMAL or BIG_DECIMAL_STRING types"); 195 | } 196 | 197 | public boolean wasNull(int columnIndex) { 198 | return cursor.isNull(columnIndex); 199 | } 200 | 201 | public ObjectCache getObjectCache() { 202 | return objectCache; 203 | } 204 | 205 | public void close() { 206 | cursor.close(); 207 | } 208 | 209 | public void closeQuietly() { 210 | close(); 211 | } 212 | 213 | /*** 214 | * Returns the underlying Android cursor object. This should not be used unless you know what you are doing. 215 | */ 216 | public Cursor getRawCursor() { 217 | return cursor; 218 | } 219 | 220 | @Override 221 | public String toString() { 222 | return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode()); 223 | } 224 | 225 | private int lookupColumn(String columnName) { 226 | // we either use linear search or our name map 227 | if (columnNameMap == null) { 228 | for (int i = 0; i < columnNames.length; i++) { 229 | // NOTE: this is case sensitive 230 | if (columnNames[i].equals(columnName)) { 231 | return i; 232 | } 233 | } 234 | return -1; 235 | } else { 236 | // NOTE: this is case sensitive 237 | Integer index = columnNameMap.get(columnName); 238 | if (index == null) { 239 | return -1; 240 | } else { 241 | return index; 242 | } 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/AndroidLog.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android; 2 | 3 | import android.util.Log; 4 | 5 | import com.j256.ormlite.logger.LoggerFactory; 6 | 7 | /** 8 | * Implementation of our logger which delegates to the internal Android logger. 9 | * 10 | *

11 | * To see log messages you will do something like: 12 | * 13 | *

 14 |  * adb shell setprop log.tag.OrmLiteBaseActivity VERBOSE
 15 |  * 
16 | * 17 | *

18 | * 19 | *

20 | * NOTE: Unfortunately, Android variables are limited in size so this class takes that last 23 (sic) characters 21 | * of the class name if it is larger than 23 characters. For example, if the class is AndroidDatabaseConnection you 22 | * would do: 23 | * 24 | *

 25 |  * adb shell setprop log.tag.droidDatabaseConnection VERBOSE
 26 |  * 
27 | * 28 | *

29 | * 30 | *

31 | * To see all ORMLite log messages use: 32 | * 33 | *

 34 |  * adb shell setprop log.tag.ORMLite DEBUG
 35 |  * 
36 | * 37 | * @author graywatson 38 | */ 39 | public class AndroidLog implements com.j256.ormlite.logger.Log { 40 | 41 | private final static String ALL_LOGS_NAME = "ORMLite"; 42 | private final static int REFRESH_LEVEL_CACHE_EVERY = 200; 43 | 44 | private final static int MAX_TAG_LENGTH = 23; 45 | private String className; 46 | 47 | // we do this because supposedly Log.isLoggable() always does IO 48 | private volatile int levelCacheC = 0; 49 | private final boolean levelCache[]; 50 | 51 | public AndroidLog(String className) { 52 | // get the last part of the class name 53 | this.className = LoggerFactory.getSimpleClassName(className); 54 | // make sure that our tag length is not too long 55 | int length = this.className.length(); 56 | if (length > MAX_TAG_LENGTH) { 57 | this.className = this.className.substring(length - MAX_TAG_LENGTH, length); 58 | } 59 | // find the maximum level value 60 | int maxLevel = 0; 61 | for (com.j256.ormlite.logger.Log.Level level : com.j256.ormlite.logger.Log.Level.values()) { 62 | int androidLevel = levelToAndroidLevel(level); 63 | if (androidLevel > maxLevel) { 64 | maxLevel = androidLevel; 65 | } 66 | } 67 | levelCache = new boolean[maxLevel + 1]; 68 | refreshLevelCache(); 69 | } 70 | 71 | public boolean isLevelEnabled(Level level) { 72 | // we don't care if this is not synchronized, it will be updated sooner or later and multiple updates are fine. 73 | if (++levelCacheC >= REFRESH_LEVEL_CACHE_EVERY) { 74 | refreshLevelCache(); 75 | levelCacheC = 0; 76 | } 77 | int androidLevel = levelToAndroidLevel(level); 78 | if (androidLevel < levelCache.length) { 79 | return levelCache[androidLevel]; 80 | } else { 81 | return isLevelEnabledInternal(androidLevel); 82 | } 83 | } 84 | 85 | public void log(Level level, String msg) { 86 | switch (level) { 87 | case TRACE : 88 | Log.v(className, msg); 89 | break; 90 | case DEBUG : 91 | Log.d(className, msg); 92 | break; 93 | case INFO : 94 | Log.i(className, msg); 95 | break; 96 | case WARNING : 97 | Log.w(className, msg); 98 | break; 99 | case ERROR : 100 | Log.e(className, msg); 101 | break; 102 | case FATAL : 103 | Log.e(className, msg); 104 | break; 105 | default : 106 | Log.i(className, msg); 107 | break; 108 | } 109 | } 110 | 111 | public void log(Level level, String msg, Throwable t) { 112 | switch (level) { 113 | case TRACE : 114 | Log.v(className, msg, t); 115 | break; 116 | case DEBUG : 117 | Log.d(className, msg, t); 118 | break; 119 | case INFO : 120 | Log.i(className, msg, t); 121 | break; 122 | case WARNING : 123 | Log.w(className, msg, t); 124 | break; 125 | case ERROR : 126 | Log.e(className, msg, t); 127 | break; 128 | case FATAL : 129 | Log.e(className, msg, t); 130 | break; 131 | default : 132 | Log.i(className, msg, t); 133 | break; 134 | } 135 | } 136 | 137 | private void refreshLevelCache() { 138 | for (com.j256.ormlite.logger.Log.Level level : com.j256.ormlite.logger.Log.Level.values()) { 139 | int androidLevel = levelToAndroidLevel(level); 140 | if (androidLevel < levelCache.length) { 141 | levelCache[androidLevel] = isLevelEnabledInternal(androidLevel); 142 | } 143 | } 144 | } 145 | 146 | private boolean isLevelEnabledInternal(int androidLevel) { 147 | // this is supposedly expensive with an IO operation for each call so we cache them into levelCache[] 148 | return Log.isLoggable(className, androidLevel) || Log.isLoggable(ALL_LOGS_NAME, androidLevel); 149 | } 150 | 151 | private int levelToAndroidLevel(com.j256.ormlite.logger.Log.Level level) { 152 | switch (level) { 153 | case TRACE : 154 | return Log.VERBOSE; 155 | case DEBUG : 156 | return Log.DEBUG; 157 | case INFO : 158 | return Log.INFO; 159 | case WARNING : 160 | return Log.WARN; 161 | case ERROR : 162 | return Log.ERROR; 163 | case FATAL : 164 | return Log.ERROR; 165 | default : 166 | return Log.INFO; 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OpenHelperManager.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | 6 | import com.j256.ormlite.dao.BaseDaoImpl; 7 | import com.j256.ormlite.dao.DaoManager; 8 | import com.j256.ormlite.logger.Logger; 9 | import com.j256.ormlite.logger.LoggerFactory; 10 | 11 | import net.sqlcipher.database.SQLiteOpenHelper; 12 | 13 | import java.lang.reflect.Constructor; 14 | import java.lang.reflect.ParameterizedType; 15 | import java.lang.reflect.Type; 16 | 17 | /** 18 | * This helps organize and access database connections to optimize connection sharing. There are several schemes to 19 | * manage the database connections in an Android app, but as an app gets more complicated, there are many potential 20 | * places where database locks can occur. This class allows database connection sharing between multiple threads in a 21 | * single app. 22 | * 23 | * This gets injected or called with the {@link OrmLiteSqliteOpenHelper} class that is used to manage the database 24 | * connection. The helper instance will be kept in a static field and only released once its internal usage count goes 25 | * to 0. 26 | * 27 | * The {@link SQLiteOpenHelper} and database classes maintain one connection under the hood, and prevent locks in the 28 | * java code. Creating multiple connections can potentially be a source of trouble. This class shares the same 29 | * connection instance between multiple clients, which will allow multiple activities and services to run at the same 30 | * time. 31 | * 32 | * Every time you use the helper, you should call {@link #getHelper(Context)} or {@link #getHelper(Context, Class)}. 33 | * When you are done with the helper you should call {@link #releaseHelper()}. 34 | * 35 | * @author graywatson, kevingalligan 36 | */ 37 | public class OpenHelperManager { 38 | 39 | private static final String HELPER_CLASS_RESOURCE_NAME = "open_helper_classname"; 40 | private static Logger logger = LoggerFactory.getLogger(OpenHelperManager.class); 41 | 42 | private static Class helperClass = null; 43 | private static volatile OrmLiteSqliteOpenHelper helper = null; 44 | private static boolean wasClosed = false; 45 | private static int instanceCount = 0; 46 | 47 | /** 48 | * If you are _not_ using the {@link OrmLiteBaseActivity} type classes then you will need to call this in a static 49 | * method in your code. 50 | */ 51 | public static synchronized void setOpenHelperClass(Class openHelperClass) { 52 | if (openHelperClass == null) { 53 | helperClass = null; 54 | } else { 55 | innerSetHelperClass(openHelperClass); 56 | } 57 | } 58 | 59 | /** 60 | * Set the helper for the manager. This is most likely used for testing purposes and should only be called if you 61 | * _really_ know what you are doing. If you do use it then it should be in a static {} initializing block to make 62 | * sure you have one helper instance for your application. 63 | */ 64 | public static synchronized void setHelper(OrmLiteSqliteOpenHelper helper) { 65 | OpenHelperManager.helper = helper; 66 | } 67 | 68 | /** 69 | * Create a static instance of our open helper from the helper class. This has a usage counter on it so make sure 70 | * all calls to this method have an associated call to {@link #releaseHelper()}. This should be called during an 71 | * onCreate() type of method when the application or service is starting. The caller should then keep the helper 72 | * around until it is shutting down when {@link #releaseHelper()} should be called. 73 | */ 74 | public static synchronized T getHelper(Context context, Class openHelperClass) { 75 | if (openHelperClass == null) { 76 | throw new IllegalArgumentException("openHelperClass argument is null"); 77 | } 78 | innerSetHelperClass(openHelperClass); 79 | return loadHelper(context, openHelperClass); 80 | } 81 | 82 | /** 83 | * Similar to {@link #getHelper(Context, Class)} (which is recommended) except we have to find the helper class 84 | * through other means. This method requires that the Context be a class that extends one of ORMLite's Android base 85 | * classes such as {@link OrmLiteBaseActivity}. Either that or the helper class needs to be set in the strings.xml. 86 | * 87 | *

88 | * To find the helper class, this does the following:
89 | * 1) If the class has been set with a call to {@link #setOpenHelperClass(Class)}, it will be used to construct a 90 | * helper.
91 | * 2) If the resource class name is configured in the strings.xml file it will be used.
92 | * 3) The context class hierarchy is walked looking at the generic parameters for a class extending 93 | * OrmLiteSqliteOpenHelper. This is used by the {@link OrmLiteBaseActivity} and other base classes.
94 | * 4) An exception is thrown saying that it was not able to set the helper class. 95 | *

96 | * 97 | * @deprecated Should use {@link #getHelper(Context, Class)} 98 | */ 99 | @Deprecated 100 | public static synchronized OrmLiteSqliteOpenHelper getHelper(Context context) { 101 | if (helperClass == null) { 102 | if (context == null) { 103 | throw new IllegalArgumentException("context argument is null"); 104 | } 105 | Context appContext = context.getApplicationContext(); 106 | innerSetHelperClass(lookupHelperClass(appContext, context.getClass())); 107 | } 108 | return loadHelper(context, helperClass); 109 | } 110 | 111 | /** 112 | * @deprecated This has been renamed to be {@link #releaseHelper()}. 113 | */ 114 | @Deprecated 115 | public static void release() { 116 | releaseHelper(); 117 | } 118 | 119 | /** 120 | * Release the helper that was previously returned by a call {@link #getHelper(Context)} or 121 | * {@link #getHelper(Context, Class)}. This will decrement the usage counter and close the helper if the counter is 122 | * 0. 123 | * 124 | *

125 | * WARNING: This should be called in an onDestroy() type of method when your application or service is 126 | * terminating or if your code is no longer going to use the helper or derived DAOs in any way. _Don't_ call this 127 | * method if you expect to call {@link #getHelper(Context)} again before the application terminates. 128 | *

129 | */ 130 | public static synchronized void releaseHelper() { 131 | instanceCount--; 132 | logger.trace("releasing helper {}, instance count = {}", helper, instanceCount); 133 | if (instanceCount <= 0) { 134 | if (helper != null) { 135 | logger.trace("zero instances, closing helper {}", helper); 136 | helper.close(); 137 | helper = null; 138 | wasClosed = true; 139 | } 140 | if (instanceCount < 0) { 141 | logger.error("too many calls to release helper, instance count = {}", instanceCount); 142 | } 143 | } 144 | } 145 | 146 | /** 147 | * Set the helper class and make sure we aren't changing it to another class. 148 | */ 149 | private static void innerSetHelperClass(Class openHelperClass) { 150 | // make sure if that there are not 2 helper classes in an application 151 | if (openHelperClass == null) { 152 | throw new IllegalStateException("Helper class was trying to be reset to null"); 153 | } else if (helperClass == null) { 154 | helperClass = openHelperClass; 155 | } else if (helperClass != openHelperClass) { 156 | throw new IllegalStateException("Helper class was " + helperClass + " but is trying to be reset to " 157 | + openHelperClass); 158 | } 159 | } 160 | 161 | private static T loadHelper(Context context, Class openHelperClass) { 162 | if (helper == null) { 163 | if (wasClosed) { 164 | // this can happen if you are calling get/release and then get again 165 | logger.info("helper was already closed and is being re-opened"); 166 | } 167 | if (context == null) { 168 | throw new IllegalArgumentException("context argument is null"); 169 | } 170 | Context appContext = context.getApplicationContext(); 171 | helper = constructHelper(appContext, openHelperClass); 172 | logger.trace("zero instances, created helper {}", helper); 173 | /* 174 | * Filipe Leandro and I worked on this bug for like 10 hours straight. It's a doosey. 175 | * 176 | * Each ForeignCollection has internal DAO objects that are holding a ConnectionSource. Each Android 177 | * ConnectionSource is tied to a particular database connection. What Filipe was seeing was that when all of 178 | * his views we closed (onDestroy), but his application WAS NOT FULLY KILLED, the first View.onCreate() 179 | * method would open a new connection to the database. Fine. But because he application was still in memory, 180 | * the static BaseDaoImpl default cache had not been cleared and was containing cached objects with 181 | * ForeignCollections. The ForeignCollections still had references to the DAOs that had been opened with old 182 | * ConnectionSource objects and therefore the old database connection. Using those cached collections would 183 | * cause exceptions saying that you were trying to work with a database that had already been close. 184 | * 185 | * Now, whenever we create a new helper object, we must make sure that the internal object caches have been 186 | * fully cleared. This is a good lesson for anyone that is holding objects around after they have closed 187 | * connections to the database or re-created the DAOs on a different connection somehow. 188 | */ 189 | BaseDaoImpl.clearAllInternalObjectCaches(); 190 | /* 191 | * Might as well do this also since if the helper changes then the ConnectionSource will change so no one is 192 | * going to have a cache hit on the old DAOs anyway. All they are doing is holding memory. 193 | * 194 | * NOTE: we don't want to clear the config map. 195 | */ 196 | DaoManager.clearDaoCache(); 197 | instanceCount = 0; 198 | } 199 | 200 | instanceCount++; 201 | logger.trace("returning helper {}, instance count = {} ", helper, instanceCount); 202 | @SuppressWarnings("unchecked") 203 | T castHelper = (T) helper; 204 | return castHelper; 205 | } 206 | 207 | /** 208 | * Call the constructor on our helper class. 209 | */ 210 | private static OrmLiteSqliteOpenHelper constructHelper(Context context, 211 | Class openHelperClass) { 212 | Constructor constructor; 213 | try { 214 | constructor = openHelperClass.getConstructor(Context.class); 215 | } catch (Exception e) { 216 | throw new IllegalStateException( 217 | "Could not find public constructor that has a single (Context) argument for helper class " 218 | + openHelperClass, e); 219 | } 220 | try { 221 | return (OrmLiteSqliteOpenHelper) constructor.newInstance(context); 222 | } catch (Exception e) { 223 | throw new IllegalStateException("Could not construct instance of helper class " + openHelperClass, e); 224 | } 225 | } 226 | 227 | /** 228 | * Lookup the helper class either from the resource string or by looking for a generic parameter. 229 | */ 230 | private static Class lookupHelperClass(Context context, Class componentClass) { 231 | 232 | // see if we have the magic resource class name set 233 | Resources resources = context.getResources(); 234 | int resourceId = resources.getIdentifier(HELPER_CLASS_RESOURCE_NAME, "string", context.getPackageName()); 235 | if (resourceId != 0) { 236 | String className = resources.getString(resourceId); 237 | try { 238 | @SuppressWarnings("unchecked") 239 | Class castClass = 240 | (Class) Class.forName(className); 241 | return castClass; 242 | } catch (Exception e) { 243 | throw new IllegalStateException("Could not create helper instance for class " + className, e); 244 | } 245 | } 246 | 247 | // try walking the context class to see if we can get the OrmLiteSqliteOpenHelper from a generic parameter 248 | for (Class componentClassWalk = componentClass; componentClassWalk != null; componentClassWalk = 249 | componentClassWalk.getSuperclass()) { 250 | Type superType = componentClassWalk.getGenericSuperclass(); 251 | if (superType == null || !(superType instanceof ParameterizedType)) { 252 | continue; 253 | } 254 | // get the generic type arguments 255 | Type[] types = ((ParameterizedType) superType).getActualTypeArguments(); 256 | // defense 257 | if (types == null || types.length == 0) { 258 | continue; 259 | } 260 | for (Type type : types) { 261 | // defense 262 | if (!(type instanceof Class)) { 263 | continue; 264 | } 265 | Class clazz = (Class) type; 266 | if (OrmLiteSqliteOpenHelper.class.isAssignableFrom(clazz)) { 267 | @SuppressWarnings("unchecked") 268 | Class castOpenHelperClass = 269 | (Class) clazz; 270 | return castOpenHelperClass; 271 | } 272 | } 273 | } 274 | throw new IllegalStateException( 275 | "Could not find OpenHelperClass because none of the generic parameters of class " + componentClass 276 | + " extends OrmLiteSqliteOpenHelper. You should use getHelper(Context, Class) instead."); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteBaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | 7 | import com.j256.ormlite.logger.Logger; 8 | import com.j256.ormlite.logger.LoggerFactory; 9 | import com.j256.ormlite.support.ConnectionSource; 10 | 11 | /** 12 | * Base class to use for activities in Android. 13 | * 14 | * You can simply call {@link #getHelper()} to get your helper class, or {@link #getConnectionSource()} to get a 15 | * {@link ConnectionSource}. 16 | * 17 | * The method {@link #getHelper()} assumes you are using the default helper factory -- see {@link OpenHelperManager}. If 18 | * not, you'll need to provide your own helper instances which will need to implement a reference counting scheme. This 19 | * method will only be called if you use the database, and only called once for this activity's life-cycle. 'close' will 20 | * also be called once for each call to createInstance. 21 | * 22 | * @author graywatson, kevingalligan 23 | */ 24 | public abstract class OrmLiteBaseActivity extends Activity { 25 | 26 | private volatile H helper; 27 | private volatile boolean created = false; 28 | private volatile boolean destroyed = false; 29 | private static Logger logger = LoggerFactory.getLogger(OrmLiteBaseActivity.class); 30 | 31 | /** 32 | * Get a helper for this action. 33 | */ 34 | public H getHelper() { 35 | if (helper == null) { 36 | if (!created) { 37 | throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null"); 38 | } else if (destroyed) { 39 | throw new IllegalStateException( 40 | "A call to onDestroy has already been made and the helper cannot be used after that point"); 41 | } else { 42 | throw new IllegalStateException("Helper is null for some unknown reason"); 43 | } 44 | } else { 45 | return helper; 46 | } 47 | } 48 | 49 | /** 50 | * Get a connection source for this action. 51 | */ 52 | public ConnectionSource getConnectionSource() { 53 | return getHelper().getConnectionSource(); 54 | } 55 | 56 | @Override 57 | protected void onCreate(Bundle savedInstanceState) { 58 | if (helper == null) { 59 | helper = getHelperInternal(this); 60 | created = true; 61 | } 62 | super.onCreate(savedInstanceState); 63 | } 64 | 65 | @Override 66 | protected void onDestroy() { 67 | super.onDestroy(); 68 | releaseHelper(helper); 69 | destroyed = true; 70 | } 71 | 72 | /** 73 | * This is called internally by the class to populate the helper object instance. This should not be called directly 74 | * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are 75 | * managing your own helper creation, override this method to supply this activity with a helper instance. 76 | * 77 | *

78 | * NOTE: If you override this method, you most likely will need to override the 79 | * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well. 80 | *

81 | */ 82 | protected H getHelperInternal(Context context) { 83 | @SuppressWarnings({ "unchecked", "deprecation" }) 84 | H newHelper = (H) OpenHelperManager.getHelper(context); 85 | logger.trace("{}: got new helper {} from OpenHelperManager", this, newHelper); 86 | return newHelper; 87 | } 88 | 89 | /** 90 | * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call 91 | * this directly since {@link #onDestroy()} does it for you. 92 | * 93 | *

94 | * NOTE: If you override this method, you most likely will need to override the 95 | * {@link #getHelperInternal(Context)} method as well. 96 | *

97 | */ 98 | protected void releaseHelper(H helper) { 99 | OpenHelperManager.releaseHelper(); 100 | logger.trace("{}: helper {} was released, set to null", this, helper); 101 | this.helper = null; 102 | } 103 | 104 | @Override 105 | public String toString() { 106 | return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode()); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteBaseActivityGroup.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.app.ActivityGroup; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | 7 | import com.j256.ormlite.support.ConnectionSource; 8 | 9 | /** 10 | * Base class to use for activity groups in Android. 11 | * 12 | * You can simply call {@link #getHelper()} to get your helper class, or {@link #getConnectionSource()} to get a 13 | * {@link ConnectionSource}. 14 | * 15 | * The method {@link #getHelper()} assumes you are using the default helper factory -- see {@link OpenHelperManager}. If 16 | * not, you'll need to provide your own helper instances which will need to implement a reference counting scheme. This 17 | * method will only be called if you use the database, and only called once for this activity's life-cycle. 'close' will 18 | * also be called once for each call to createInstance. 19 | * 20 | * @author graywatson, kevingalligan 21 | */ 22 | public abstract class OrmLiteBaseActivityGroup extends ActivityGroup { 23 | 24 | private volatile H helper; 25 | private volatile boolean created = false; 26 | private volatile boolean destroyed = false; 27 | 28 | /** 29 | * Get a helper for this action. 30 | */ 31 | public H getHelper() { 32 | if (helper == null) { 33 | if (!created) { 34 | throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null"); 35 | } else if (destroyed) { 36 | throw new IllegalStateException( 37 | "A call to onDestroy has already been made and the helper cannot be used after that point"); 38 | } else { 39 | throw new IllegalStateException("Helper is null for some unknown reason"); 40 | } 41 | } else { 42 | return helper; 43 | } 44 | } 45 | 46 | /** 47 | * Get a connection source for this action. 48 | */ 49 | public ConnectionSource getConnectionSource() { 50 | return getHelper().getConnectionSource(); 51 | } 52 | 53 | @Override 54 | protected void onCreate(Bundle savedInstanceState) { 55 | if (helper == null) { 56 | helper = getHelperInternal(this); 57 | created = true; 58 | } 59 | super.onCreate(savedInstanceState); 60 | } 61 | 62 | @Override 63 | protected void onDestroy() { 64 | super.onDestroy(); 65 | releaseHelper(helper); 66 | destroyed = true; 67 | } 68 | 69 | /** 70 | * This is called internally by the class to populate the helper object instance. This should not be called directly 71 | * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are 72 | * managing your own helper creation, override this method to supply this activity with a helper instance. 73 | * 74 | *

75 | * NOTE: If you override this method, you most likely will need to override the 76 | * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well. 77 | *

78 | */ 79 | protected H getHelperInternal(Context context) { 80 | @SuppressWarnings({ "unchecked", "deprecation" }) 81 | H newHelper = (H) OpenHelperManager.getHelper(context); 82 | return newHelper; 83 | } 84 | 85 | /** 86 | * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call 87 | * this directly since {@link #onDestroy()} does it for you. 88 | * 89 | *

90 | * NOTE: If you override this method, you most likely will need to override the 91 | * {@link #getHelperInternal(Context)} method as well. 92 | *

93 | */ 94 | protected void releaseHelper(H helper) { 95 | OpenHelperManager.releaseHelper(); 96 | this.helper = null; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteBaseListActivity.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.app.ListActivity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | 7 | import com.j256.ormlite.support.ConnectionSource; 8 | 9 | /** 10 | * Base class to use for Tab activities in Android. 11 | * 12 | * For more information, see {@link OrmLiteBaseActivity}. 13 | * 14 | * @author graywatson, kevingalligan 15 | */ 16 | public abstract class OrmLiteBaseListActivity extends ListActivity { 17 | 18 | private volatile H helper; 19 | private volatile boolean created = false; 20 | private volatile boolean destroyed = false; 21 | 22 | /** 23 | * Get a helper for this action. 24 | */ 25 | public H getHelper() { 26 | if (helper == null) { 27 | if (!created) { 28 | throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null"); 29 | } else if (destroyed) { 30 | throw new IllegalStateException( 31 | "A call to onDestroy has already been made and the helper cannot be used after that point"); 32 | } else { 33 | throw new IllegalStateException("Helper is null for some unknown reason"); 34 | } 35 | } else { 36 | return helper; 37 | } 38 | } 39 | 40 | /** 41 | * Get a connection source for this action. 42 | */ 43 | public ConnectionSource getConnectionSource() { 44 | return getHelper().getConnectionSource(); 45 | } 46 | 47 | @Override 48 | protected void onCreate(Bundle savedInstanceState) { 49 | if (helper == null) { 50 | helper = getHelperInternal(this); 51 | created = true; 52 | } 53 | super.onCreate(savedInstanceState); 54 | } 55 | 56 | @Override 57 | protected void onDestroy() { 58 | super.onDestroy(); 59 | releaseHelper(helper); 60 | destroyed = true; 61 | } 62 | 63 | /** 64 | * This is called internally by the class to populate the helper object instance. This should not be called directly 65 | * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are 66 | * managing your own helper creation, override this method to supply this activity with a helper instance. 67 | * 68 | *

69 | * NOTE: If you override this method, you most likely will need to override the 70 | * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well. 71 | *

72 | */ 73 | protected H getHelperInternal(Context context) { 74 | @SuppressWarnings({ "unchecked", "deprecation" }) 75 | H newHelper = (H) OpenHelperManager.getHelper(context); 76 | return newHelper; 77 | } 78 | 79 | /** 80 | * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call 81 | * this directly since {@link #onDestroy()} does it for you. 82 | * 83 | *

84 | * NOTE: If you override this method, you most likely will need to override the 85 | * {@link #getHelperInternal(Context)} method as well. 86 | *

87 | */ 88 | protected void releaseHelper(H helper) { 89 | OpenHelperManager.releaseHelper(); 90 | this.helper = null; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteBaseService.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.app.Service; 4 | import android.content.Context; 5 | 6 | import com.j256.ormlite.support.ConnectionSource; 7 | 8 | /** 9 | * Base class to use for services in Android. 10 | * 11 | * For more information, see {@link OrmLiteBaseActivity}. 12 | * 13 | * @author graywatson, kevingalligan 14 | */ 15 | public abstract class OrmLiteBaseService extends Service { 16 | 17 | private volatile H helper; 18 | private volatile boolean created = false; 19 | private volatile boolean destroyed = false; 20 | 21 | /** 22 | * Get a helper for this action. 23 | */ 24 | public H getHelper() { 25 | if (helper == null) { 26 | if (!created) { 27 | throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null"); 28 | } else if (destroyed) { 29 | throw new IllegalStateException( 30 | "A call to onDestroy has already been made and the helper cannot be used after that point"); 31 | } else { 32 | throw new IllegalStateException("Helper is null for some unknown reason"); 33 | } 34 | } else { 35 | return helper; 36 | } 37 | } 38 | 39 | /** 40 | * Get a connection source for this action. 41 | */ 42 | public ConnectionSource getConnectionSource() { 43 | return getHelper().getConnectionSource(); 44 | } 45 | 46 | @Override 47 | public void onCreate() { 48 | if (helper == null) { 49 | helper = getHelperInternal(this); 50 | created = true; 51 | } 52 | super.onCreate(); 53 | } 54 | 55 | @Override 56 | public void onDestroy() { 57 | super.onDestroy(); 58 | releaseHelper(helper); 59 | destroyed = true; 60 | } 61 | 62 | /** 63 | * This is called internally by the class to populate the helper object instance. This should not be called directly 64 | * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are 65 | * managing your own helper creation, override this method to supply this activity with a helper instance. 66 | * 67 | *

68 | * NOTE: If you override this method, you most likely will need to override the 69 | * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well. 70 | *

71 | */ 72 | protected H getHelperInternal(Context context) { 73 | @SuppressWarnings({ "unchecked", "deprecation" }) 74 | H newHelper = (H) OpenHelperManager.getHelper(context); 75 | return newHelper; 76 | } 77 | 78 | /** 79 | * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call 80 | * this directly since {@link #onDestroy()} does it for you. 81 | * 82 | *

83 | * NOTE: If you override this method, you most likely will need to override the 84 | * {@link #getHelperInternal(Context)} method as well. 85 | *

86 | */ 87 | protected void releaseHelper(H helper) { 88 | OpenHelperManager.releaseHelper(); 89 | this.helper = null; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteBaseTabActivity.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.app.TabActivity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | 7 | import com.j256.ormlite.support.ConnectionSource; 8 | 9 | /** 10 | * Base class to use for Tab activities in Android. 11 | * 12 | * For more information, see {@link OrmLiteBaseActivity}. 13 | * 14 | * @author graywatson, kevingalligan 15 | */ 16 | public abstract class OrmLiteBaseTabActivity extends TabActivity { 17 | 18 | private volatile H helper; 19 | private volatile boolean created = false; 20 | private volatile boolean destroyed = false; 21 | 22 | /** 23 | * Get a helper for this action. 24 | */ 25 | public H getHelper() { 26 | if (helper == null) { 27 | if (!created) { 28 | throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null"); 29 | } else if (destroyed) { 30 | throw new IllegalStateException( 31 | "A call to onDestroy has already been made and the helper cannot be used after that point"); 32 | } else { 33 | throw new IllegalStateException("Helper is null for some unknown reason"); 34 | } 35 | } else { 36 | return helper; 37 | } 38 | } 39 | 40 | /** 41 | * Get a connection source for this action. 42 | */ 43 | public ConnectionSource getConnectionSource() { 44 | return getHelper().getConnectionSource(); 45 | } 46 | 47 | @Override 48 | protected void onCreate(Bundle savedInstanceState) { 49 | if (helper == null) { 50 | helper = getHelperInternal(this); 51 | created = true; 52 | } 53 | super.onCreate(savedInstanceState); 54 | } 55 | 56 | @Override 57 | protected void onDestroy() { 58 | super.onDestroy(); 59 | releaseHelper(helper); 60 | destroyed = true; 61 | } 62 | 63 | /** 64 | * This is called internally by the class to populate the helper object instance. This should not be called directly 65 | * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are 66 | * managing your own helper creation, override this method to supply this activity with a helper instance. 67 | * 68 | *

69 | * NOTE: If you override this method, you most likely will need to override the 70 | * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well. 71 | *

72 | * 73 | * @see OpenHelperManager#getHelper(Context) 74 | */ 75 | protected H getHelperInternal(Context context) { 76 | @SuppressWarnings({ "unchecked", "deprecation" }) 77 | H newHelper = (H) OpenHelperManager.getHelper(context); 78 | return newHelper; 79 | } 80 | 81 | /** 82 | * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call 83 | * this directly since {@link #onDestroy()} does it for you. 84 | * 85 | *

86 | * NOTE: If you override this method, you most likely will need to override the 87 | * {@link #getHelperInternal(Context)} method as well. 88 | *

89 | */ 90 | protected void releaseHelper(H helper) { 91 | OpenHelperManager.releaseHelper(); 92 | this.helper = null; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteConfigUtil.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import com.j256.ormlite.dao.DaoManager; 4 | import com.j256.ormlite.db.DatabaseType; 5 | import com.j256.ormlite.field.DatabaseField; 6 | import com.j256.ormlite.field.DatabaseFieldConfig; 7 | import com.j256.ormlite.field.ForeignCollectionField; 8 | import com.j256.ormlite.sqlcipher.db.SqliteAndroidDatabaseType; 9 | import com.j256.ormlite.table.DatabaseTable; 10 | import com.j256.ormlite.table.DatabaseTableConfig; 11 | import com.j256.ormlite.table.DatabaseTableConfigLoader; 12 | 13 | import net.sqlcipher.database.SQLiteDatabase; 14 | 15 | import java.io.BufferedReader; 16 | import java.io.BufferedWriter; 17 | import java.io.File; 18 | import java.io.FileFilter; 19 | import java.io.FileOutputStream; 20 | import java.io.FileReader; 21 | import java.io.IOException; 22 | import java.io.OutputStream; 23 | import java.io.OutputStreamWriter; 24 | import java.lang.reflect.Field; 25 | import java.sql.SQLException; 26 | import java.text.SimpleDateFormat; 27 | import java.util.ArrayList; 28 | import java.util.Date; 29 | import java.util.List; 30 | 31 | /** 32 | * Database configuration file helper class that is used to write a configuration file into the raw resource 33 | * sub-directory to speed up DAO creation. 34 | * 35 | *

36 | * With help from the user list and especially Ian Dees, we discovered that calls to annotation methods in Android are 37 | * _very_ expensive because Method.equals() was doing a huge toString(). This was causing folks to see 2-3 seconds 38 | * startup time when configuring 10-15 DAOs because of 1000s of calls to @DatabaseField methods. See this Android bug report. 40 | *

41 | * 42 | *

43 | * I added this utility class which writes a configuration file into the raw resource "res/raw" directory inside of your 44 | * project containing the table and field names and associated details. This file can then be loaded into the 45 | * {@link DaoManager} with the help of the 46 | * {@link OrmLiteSqliteOpenHelper#OrmLiteSqliteOpenHelper(android.content.Context, String, SQLiteDatabase.CursorFactory, int, int)} 47 | * constructor. This means that you can configure your classes _without_ any runtime calls to annotations. It seems 48 | * significantly faster. 49 | *

50 | * 51 | *

52 | * WARNING: Although this is fast, the big problem is that you have to remember to regenerate the config file 53 | * whenever you edit one of your database classes. There is no way that I know of to do this automagically. 54 | *

55 | * 56 | * @author graywatson 57 | */ 58 | public class OrmLiteConfigUtil { 59 | 60 | /** 61 | * Resource directory name that we are looking for. 62 | */ 63 | protected static final String RESOURCE_DIR_NAME = "res"; 64 | /** 65 | * Raw directory name that we are looking for. 66 | */ 67 | protected static final String RAW_DIR_NAME = "raw"; 68 | 69 | /** 70 | * Maximum recursion level while we are looking for source files. 71 | */ 72 | protected static int maxFindSourceLevel = 20; 73 | 74 | private static final DatabaseType databaseType = new SqliteAndroidDatabaseType(); 75 | 76 | /** 77 | * A call through to {@link #writeConfigFile(String)} taking the file name from the single command line argument. 78 | */ 79 | public static void main(String[] args) throws Exception { 80 | if (args.length != 1) { 81 | throw new IllegalArgumentException("Main can take a single file-name argument."); 82 | } 83 | writeConfigFile(args[0]); 84 | } 85 | 86 | /** 87 | * Finds the annotated classes in the current directory or below and writes a configuration file to the file-name in 88 | * the raw folder. 89 | */ 90 | public static void writeConfigFile(String fileName) throws SQLException, IOException { 91 | List> classList = new ArrayList>(); 92 | findAnnotatedClasses(classList, new File("."), 0); 93 | writeConfigFile(fileName, classList.toArray(new Class[classList.size()])); 94 | } 95 | 96 | /** 97 | * Writes a configuration fileName in the raw directory with the configuration for classes. 98 | */ 99 | public static void writeConfigFile(String fileName, Class[] classes) throws SQLException, IOException { 100 | File rawDir = findRawDir(new File(".")); 101 | if (rawDir == null) { 102 | System.err.println("Could not find " + RAW_DIR_NAME + " directory which is typically in the " 103 | + RESOURCE_DIR_NAME + " directory"); 104 | } else { 105 | File configFile = new File(rawDir, fileName); 106 | writeConfigFile(configFile, classes); 107 | } 108 | } 109 | 110 | /** 111 | * Finds the annotated classes in the current directory or below and writes a configuration file. 112 | */ 113 | public static void writeConfigFile(File configFile) throws SQLException, IOException { 114 | writeConfigFile(configFile, new File(".")); 115 | } 116 | 117 | /** 118 | * Finds the annotated classes in the specified search directory or below and writes a configuration file. 119 | */ 120 | public static void writeConfigFile(File configFile, File searchDir) throws SQLException, IOException { 121 | List> classList = new ArrayList>(); 122 | findAnnotatedClasses(classList, searchDir, 0); 123 | writeConfigFile(configFile, classList.toArray(new Class[classList.size()])); 124 | } 125 | 126 | /** 127 | * Write a configuration file with the configuration for classes. 128 | */ 129 | public static void writeConfigFile(File configFile, Class[] classes) throws SQLException, IOException { 130 | System.out.println("Writing configurations to " + configFile.getAbsolutePath()); 131 | writeConfigFile(new FileOutputStream(configFile), classes); 132 | } 133 | 134 | /** 135 | * Write a configuration file to an output stream with the configuration for classes. 136 | */ 137 | public static void writeConfigFile(OutputStream outputStream, File searchDir) throws SQLException, IOException { 138 | List> classList = new ArrayList>(); 139 | findAnnotatedClasses(classList, searchDir, 0); 140 | writeConfigFile(outputStream, classList.toArray(new Class[classList.size()])); 141 | } 142 | 143 | /** 144 | * Write a configuration file to an output stream with the configuration for classes. 145 | */ 146 | public static void writeConfigFile(OutputStream outputStream, Class[] classes) throws SQLException, IOException { 147 | BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream), 4096); 148 | try { 149 | writeHeader(writer); 150 | for (Class clazz : classes) { 151 | writeConfigForTable(writer, clazz); 152 | } 153 | // NOTE: done is here because this is public 154 | System.out.println("Done."); 155 | } finally { 156 | writer.close(); 157 | } 158 | } 159 | 160 | /** 161 | * Look for the resource-directory in the current directory or the directories above. Then look for the 162 | * raw-directory underneath the resource-directory. 163 | */ 164 | protected static File findRawDir(File dir) { 165 | for (int i = 0; dir != null && i < 20; i++) { 166 | File rawDir = findResRawDir(dir); 167 | if (rawDir != null) { 168 | return rawDir; 169 | } 170 | dir = dir.getParentFile(); 171 | } 172 | return null; 173 | } 174 | 175 | private static void writeHeader(BufferedWriter writer) throws IOException { 176 | writer.append('#'); 177 | writer.newLine(); 178 | writer.append("# generated on ").append(new SimpleDateFormat("yyyy/MM/dd hh:mm:ss").format(new Date())); 179 | writer.newLine(); 180 | writer.append('#'); 181 | writer.newLine(); 182 | } 183 | 184 | private static void findAnnotatedClasses(List> classList, File dir, int level) throws SQLException, 185 | IOException { 186 | for (File file : dir.listFiles()) { 187 | if (file.isDirectory()) { 188 | // recurse if we aren't deep enough 189 | if (level < maxFindSourceLevel) { 190 | findAnnotatedClasses(classList, file, level + 1); 191 | } 192 | continue; 193 | } 194 | // skip non .java files 195 | if (!file.getName().endsWith(".java")) { 196 | continue; 197 | } 198 | String packageName = getPackageOfClass(file); 199 | if (packageName == null) { 200 | System.err.println("Could not find package name for: " + file); 201 | continue; 202 | } 203 | // get the filename and cut off the .java 204 | String name = file.getName(); 205 | name = name.substring(0, name.length() - ".java".length()); 206 | String className = packageName + "." + name; 207 | Class clazz; 208 | try { 209 | clazz = Class.forName(className); 210 | } catch (Throwable t) { 211 | // amazingly, this sometimes throws an Error 212 | System.err.println("Could not load class file for: " + file); 213 | System.err.println(" " + t); 214 | continue; 215 | } 216 | if (classHasAnnotations(clazz)) { 217 | classList.add(clazz); 218 | } 219 | // handle inner classes 220 | try { 221 | for (Class innerClazz : clazz.getDeclaredClasses()) { 222 | if (classHasAnnotations(innerClazz)) { 223 | classList.add(innerClazz); 224 | } 225 | } 226 | } catch (Throwable t) { 227 | // amazingly, this sometimes throws an Error 228 | System.err.println("Could not load inner classes for: " + clazz); 229 | System.err.println(" " + t); 230 | continue; 231 | } 232 | } 233 | } 234 | 235 | private static void writeConfigForTable(BufferedWriter writer, Class clazz) throws SQLException, IOException { 236 | String tableName = DatabaseTableConfig.extractTableName(clazz); 237 | List fieldConfigs = new ArrayList(); 238 | // walk up the classes finding the fields 239 | try { 240 | for (Class working = clazz; working != null; working = working.getSuperclass()) { 241 | for (Field field : working.getDeclaredFields()) { 242 | DatabaseFieldConfig fieldConfig = DatabaseFieldConfig.fromField(databaseType, tableName, field); 243 | if (fieldConfig != null) { 244 | fieldConfigs.add(fieldConfig); 245 | } 246 | } 247 | } 248 | } catch (Error e) { 249 | System.err.println("Skipping " + clazz + " because we got an error finding its definition: " 250 | + e.getMessage()); 251 | return; 252 | } 253 | if (fieldConfigs.isEmpty()) { 254 | System.out.println("Skipping " + clazz + " because no annotated fields found"); 255 | return; 256 | } 257 | @SuppressWarnings({ "rawtypes", "unchecked" }) 258 | DatabaseTableConfig tableConfig = new DatabaseTableConfig(clazz, tableName, fieldConfigs); 259 | DatabaseTableConfigLoader.write(writer, tableConfig); 260 | writer.append("#################################"); 261 | writer.newLine(); 262 | System.out.println("Wrote config for " + clazz); 263 | } 264 | 265 | private static boolean classHasAnnotations(Class clazz) { 266 | while (clazz != null) { 267 | if (clazz.getAnnotation(DatabaseTable.class) != null) { 268 | return true; 269 | } 270 | Field[] fields; 271 | try { 272 | fields = clazz.getDeclaredFields(); 273 | } catch (Throwable t) { 274 | // amazingly, this sometimes throws an Error 275 | System.err.println("Could not load get delcared fields from: " + clazz); 276 | System.err.println(" " + t); 277 | return false; 278 | } 279 | for (Field field : fields) { 280 | if (field.getAnnotation(DatabaseField.class) != null 281 | || field.getAnnotation(ForeignCollectionField.class) != null) { 282 | return true; 283 | } 284 | } 285 | try { 286 | clazz = clazz.getSuperclass(); 287 | } catch (Throwable t) { 288 | // amazingly, this sometimes throws an Error 289 | System.err.println("Could not get super class for: " + clazz); 290 | System.err.println(" " + t); 291 | return false; 292 | } 293 | } 294 | 295 | return false; 296 | } 297 | 298 | /** 299 | * Returns the package name of a file that has one of the annotations we are looking for. 300 | * 301 | * @return Package prefix string or null or no annotations. 302 | */ 303 | private static String getPackageOfClass(File file) throws IOException { 304 | BufferedReader reader = new BufferedReader(new FileReader(file)); 305 | try { 306 | while (true) { 307 | String line = reader.readLine(); 308 | if (line == null) { 309 | return null; 310 | } 311 | if (line.contains("package")) { 312 | String[] parts = line.split("[ \t;]"); 313 | if (parts.length > 1 && parts[0].equals("package")) { 314 | return parts[1]; 315 | } 316 | } 317 | } 318 | } finally { 319 | reader.close(); 320 | } 321 | } 322 | 323 | /** 324 | * Look for the resource directory with raw beneath it. 325 | */ 326 | private static File findResRawDir(File dir) { 327 | for (File file : dir.listFiles()) { 328 | if (file.getName().equals(RESOURCE_DIR_NAME) && file.isDirectory()) { 329 | File[] rawFiles = file.listFiles(new FileFilter() { 330 | public boolean accept(File file) { 331 | return file.getName().equals(RAW_DIR_NAME) && file.isDirectory(); 332 | } 333 | }); 334 | if (rawFiles.length == 1) { 335 | return rawFiles[0]; 336 | } 337 | } 338 | } 339 | return null; 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/compat/ApiCompatibility.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.compat; 2 | 3 | 4 | import net.sqlcipher.Cursor; 5 | import net.sqlcipher.database.SQLiteDatabase; 6 | 7 | /** 8 | * Compatibility interface to support various different versions of the Android API. 9 | * 10 | * @author graywatson 11 | */ 12 | public interface ApiCompatibility { 13 | 14 | /** 15 | * Perform a raw query on a database with an optional cancellation-hook. 16 | */ 17 | public Cursor rawQuery(SQLiteDatabase db, String sql, String[] selectionArgs, CancellationHook cancellationHook); 18 | 19 | /** 20 | * Return a cancellation hook object that will be passed to the 21 | * {@link #rawQuery(SQLiteDatabase, String, String[], CancellationHook)}. If not supported then this will return 22 | * null. 23 | */ 24 | public CancellationHook createCancellationHook(); 25 | 26 | /** 27 | * Cancellation hook class returned by {@link ApiCompatibility#createCancellationHook()}. 28 | */ 29 | public interface CancellationHook { 30 | /** 31 | * Cancel the associated query. 32 | */ 33 | public void cancel(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/compat/ApiCompatibilityUtils.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.compat; 2 | 3 | import android.os.Build; 4 | 5 | 6 | /** 7 | * Utility class which loads the various classes based on which API version is being supported. 8 | * 9 | * @author graywatson 10 | */ 11 | @SuppressWarnings("unused") 12 | public class ApiCompatibilityUtils { 13 | 14 | private static ApiCompatibility compatibility; 15 | 16 | /** 17 | * Copied from {@link Build.VERSION_CODES}. We don't use those codes because they won't be in certain versions of 18 | * Build. 19 | */ 20 | private static final int BASE = 1; 21 | private static final int BASE_1_1 = 2; 22 | private static final int CUPCAKE = 3; 23 | private static final int DONUT = 4; 24 | private static final int ECLAIR = 5; 25 | private static final int ECLAIR_0_1 = 6; 26 | private static final int ECLAIR_MR1 = 7; 27 | private static final int FROYO = 8; 28 | private static final int GINGERBREAD = 9; 29 | private static final int GINGERBREAD_MR1 = 10; 30 | private static final int HONEYCOMB = 11; 31 | private static final int HONEYCOMB_MR1 = 12; 32 | private static final int HONEYCOMB_MR2 = 13; 33 | private static final int ICE_CREAM_SANDWICH = 14; 34 | private static final int ICE_CREAM_SANDWICH_MR1 = 15; 35 | private static final int JELLY_BEAN = 16; 36 | private static final int JELLY_BEAN_MR1 = 17; 37 | private static final int JELLY_BEAN_MR2 = 18; 38 | 39 | static { 40 | if (Build.VERSION.SDK_INT >= JELLY_BEAN) { 41 | compatibility = new com.j256.ormlite.sqlcipher.android.compat.JellyBeanApiCompatibility(); 42 | } else { 43 | compatibility = new com.j256.ormlite.sqlcipher.android.compat.BasicApiCompatibility(); 44 | } 45 | } 46 | 47 | /** 48 | * Return the compatibility class that matches our build number. 49 | */ 50 | public static ApiCompatibility getCompatibility() { 51 | return compatibility; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/compat/BasicApiCompatibility.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.compat; 2 | 3 | 4 | import net.sqlcipher.Cursor; 5 | import net.sqlcipher.database.SQLiteDatabase; 6 | 7 | /** 8 | * Basic class which provides no-op methods for all Android version. 9 | * 10 | * @author graywatson 11 | */ 12 | public class BasicApiCompatibility implements ApiCompatibility { 13 | 14 | public Cursor rawQuery(SQLiteDatabase db, String sql, String[] selectionArgs, CancellationHook cancellationHook) { 15 | // NOTE: cancellationHook will always be null 16 | return db.rawQuery(sql, selectionArgs); 17 | } 18 | 19 | public CancellationHook createCancellationHook() { 20 | return null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/android/compat/JellyBeanApiCompatibility.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.compat; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.os.CancellationSignal; 6 | 7 | import net.sqlcipher.Cursor; 8 | import net.sqlcipher.database.SQLiteDatabase; 9 | 10 | /** 11 | * Basic class which provides no-op methods for all Android version. 12 | *

13 | *

14 | * NOTE: Will show as in error if compiled with previous Android versions. 15 | *

16 | * 17 | * @author graywatson 18 | */ 19 | public class JellyBeanApiCompatibility extends BasicApiCompatibility { 20 | 21 | @Override 22 | public Cursor rawQuery(SQLiteDatabase db, String sql, String[] selectionArgs, CancellationHook cancellationHook) { 23 | // NOTE: in patched version this is the same as BasicApiCompatibility 24 | // because SqlCipher supports Android version lower than API level 16 (Jelly Bean) 25 | // if (cancellationHook == null) { 26 | return db.rawQuery(sql, selectionArgs); 27 | // } else { 28 | // return db.rawQuery(sql, selectionArgs, ((JellyBeanCancellationHook) cancellationHook).cancellationSignal); 29 | // } 30 | } 31 | 32 | @Override 33 | public CancellationHook createCancellationHook() { 34 | return new JellyBeanCancellationHook(); 35 | } 36 | 37 | protected static class JellyBeanCancellationHook implements CancellationHook { 38 | 39 | private final CancellationSignal cancellationSignal; 40 | 41 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 42 | public JellyBeanCancellationHook() { 43 | this.cancellationSignal = new CancellationSignal(); 44 | } 45 | 46 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 47 | public void cancel() { 48 | cancellationSignal.cancel(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/java/com/j256/ormlite/sqlcipher/db/SqliteAndroidDatabaseType.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.db; 2 | 3 | import com.j256.ormlite.db.BaseSqliteDatabaseType; 4 | import com.j256.ormlite.field.DataPersister; 5 | import com.j256.ormlite.field.FieldConverter; 6 | import com.j256.ormlite.field.FieldType; 7 | import com.j256.ormlite.field.types.DateStringType; 8 | import com.j256.ormlite.sqlcipher.android.DatabaseTableConfigUtil; 9 | import com.j256.ormlite.support.ConnectionSource; 10 | import com.j256.ormlite.table.DatabaseTableConfig; 11 | 12 | import java.sql.SQLException; 13 | 14 | /** 15 | * Sqlite database type information for the Android OS that makes native calls to the Android OS database APIs. 16 | * 17 | * @author graywatson 18 | */ 19 | public class SqliteAndroidDatabaseType extends BaseSqliteDatabaseType { 20 | 21 | @Override 22 | public void loadDriver() { 23 | // noop 24 | } 25 | 26 | public boolean isDatabaseUrlThisType(String url, String dbTypePart) { 27 | // not used by the android code 28 | return true; 29 | } 30 | 31 | @Override 32 | protected String getDriverClassName() { 33 | // no driver to load in android-land 34 | return null; 35 | } 36 | 37 | public String getDatabaseName() { 38 | return "Android SQLite"; 39 | } 40 | 41 | @Override 42 | protected void appendDateType(StringBuilder sb, FieldType fieldType, int fieldWidth) { 43 | // default is to store the date as a string 44 | appendStringType(sb, fieldType, fieldWidth); 45 | } 46 | 47 | @Override 48 | protected void appendBooleanType(StringBuilder sb, FieldType fieldType, int fieldWidth) { 49 | // we have to convert booleans to numbers 50 | appendShortType(sb, fieldType, fieldWidth); 51 | } 52 | 53 | @Override 54 | public FieldConverter getFieldConverter(DataPersister dataPersister) { 55 | // we are only overriding certain types 56 | switch (dataPersister.getSqlType()) { 57 | case DATE : 58 | return DateStringType.getSingleton(); 59 | default : 60 | return super.getFieldConverter(dataPersister); 61 | } 62 | } 63 | 64 | @Override 65 | public boolean isNestedSavePointsSupported() { 66 | return false; 67 | } 68 | 69 | @Override 70 | public boolean isBatchUseTransaction() { 71 | return true; 72 | } 73 | 74 | @Override 75 | public DatabaseTableConfig extractDatabaseTableConfig(ConnectionSource connectionSource, Class clazz) 76 | throws SQLException { 77 | return DatabaseTableConfigUtil.fromClass(connectionSource, clazz); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /sqlcipher-old/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | database 3 | 4 | -------------------------------------------------------------------------------- /sqlcipher/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sqlcipher/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply from: 'maven_upload.gradle' 3 | apply from: '../config.gradle' 4 | 5 | android { 6 | compileSdkVersion compileSdkVer 7 | buildToolsVersion buildToolsVer 8 | 9 | defaultConfig { 10 | minSdkVersion minSdkVer 11 | targetSdkVersion targetSdkVer 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | 22 | } 23 | 24 | repositories { 25 | flatDir { 26 | dirs 'libs' //this way we can find the .aar file in libs folder 27 | } 28 | } 29 | 30 | dependencies { 31 | compile 'net.zetetic:android-database-sqlcipher:3.5.4' 32 | compile 'com.j256.ormlite:ormlite-core:4.48' 33 | } 34 | -------------------------------------------------------------------------------- /sqlcipher/maven_pom.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=android 2 | POM_DESCRIPTION=app sql cipher lib 3 | POM_GROUP=com.xtc.data 4 | POM_ARTIFACT_ID=data-new-sqlcipher 5 | POM_PACKAGING=aar 6 | POM_VERSION=0.0.2-SNAPSHOT -------------------------------------------------------------------------------- /sqlcipher/maven_upload.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven' 2 | 3 | Properties user_properties = new Properties() 4 | user_properties.load(project.rootProject.file('maven_user.properties').newDataInputStream()) 5 | def repoUrl = user_properties.getProperty("repository.url") 6 | def userName = user_properties.getProperty("repository.user") 7 | def userPassword = user_properties.getProperty("repository.password") 8 | 9 | Properties pom_properties = new Properties() 10 | pom_properties.load(project.file('maven_pom.properties').newDataInputStream()) 11 | def pom_name = pom_properties.getProperty("POM_NAME") 12 | def pom_description = pom_properties.getProperty("POM_DESCRIPTION") 13 | def pom_group = pom_properties.getProperty("POM_GROUP") 14 | def pom_artifact_id = pom_properties.getProperty("POM_ARTIFACT_ID") 15 | def pom_packaging = pom_properties.getProperty("POM_PACKAGING") 16 | def pom_version = pom_properties.getProperty("POM_VERSION") 17 | 18 | 19 | uploadArchives { 20 | 21 | repositories.mavenDeployer { 22 | repository(url: repoUrl) { 23 | authentication(userName: userName, 24 | password: userPassword) 25 | } 26 | 27 | pom.project { 28 | name pom_name 29 | description pom_description 30 | groupId pom_group 31 | artifactId pom_artifact_id 32 | version pom_version 33 | packaging pom_packaging 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /sqlcipher/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Stay/develop/android-sdk-mac_x86/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sqlcipher/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/DatabaseEncrypted.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import net.sqlcipher.database.SQLiteDatabase; 7 | 8 | import java.io.File; 9 | 10 | 11 | /** 12 | * 数据库加密 13 | */ 14 | public class DatabaseEncrypted { 15 | 16 | private static final String TAG = "DatabaseEncrypted"; 17 | 18 | /** 19 | * 将未加密数据库转换为加密数据库 20 | * 21 | * @param application application 22 | * @param unencryptedDbName 未加密数据库名 23 | * @param encryptedDbName 加密数据库名 24 | * @param password 加密数据库密码 25 | */ 26 | public static boolean importUnencryptedDatabase(Application application, String unencryptedDbName, 27 | String encryptedDbName, String password) { 28 | File unencryptedFile = application.getDatabasePath(unencryptedDbName); 29 | if (!unencryptedFile.exists()) { 30 | Log.i(TAG, "unencrypted database not exist"); 31 | return true; 32 | } 33 | 34 | File encryptedFile = application.getDatabasePath(encryptedDbName); 35 | if (encryptedFile.exists()) { 36 | Log.i(TAG, "encrypted database is exist"); 37 | return true; 38 | } 39 | 40 | Log.i(TAG, "encrypted database"); 41 | SQLiteDatabase database = null; 42 | try { 43 | database = SQLiteDatabase.openOrCreateDatabase(unencryptedFile, "", null); 44 | 45 | database.rawExecSQL(String.format("ATTACH DATABASE '%s' AS encrypted KEY '%s'", 46 | encryptedFile.getAbsolutePath(), password)); 47 | database.rawExecSQL("select sqlcipher_export('encrypted')"); 48 | database.rawExecSQL("DETACH DATABASE encrypted"); 49 | 50 | return true; 51 | } catch (Exception e) { 52 | e.printStackTrace(); 53 | return false; 54 | } finally { 55 | if (database != null) { 56 | database.close(); 57 | } 58 | application.deleteDatabase(unencryptedDbName); 59 | SQLiteDatabase.releaseMemory(); 60 | } 61 | } 62 | 63 | /** 64 | * 将加密数据库转换为常规数据库 65 | * 66 | * @param application application 67 | * @param encryptedDbName 加密数据库名 68 | * @param unencryptedDbName 未加密数据库名 69 | * @param password 加密数据库密码 70 | */ 71 | public static boolean exportToUnencryptedDatabase(Application application, String encryptedDbName, String unencryptedDbName, String password) { 72 | File encryptedFile = application.getDatabasePath(encryptedDbName); 73 | if (!encryptedFile.exists()) { 74 | Log.i(TAG, "encrypted database not exist"); 75 | return true; 76 | } 77 | 78 | File unencryptedFile = application.getDatabasePath(unencryptedDbName); 79 | if (unencryptedFile.exists()) { 80 | Log.i(TAG, "unencrypted database is exist"); 81 | return true; 82 | } 83 | 84 | Log.i(TAG, "unencrypted database"); 85 | SQLiteDatabase database = null; 86 | try { 87 | database = SQLiteDatabase.openOrCreateDatabase(encryptedFile, password, null); 88 | 89 | application.deleteDatabase(unencryptedDbName); 90 | database.rawExecSQL(String.format("ATTACH DATABASE '%s' as plaintext KEY '';", 91 | unencryptedFile.getAbsolutePath())); 92 | database.rawExecSQL("SELECT sqlcipher_export('plaintext');"); 93 | database.rawExecSQL("DETACH DATABASE plaintext;"); 94 | 95 | return true; 96 | } catch (Exception e) { 97 | e.printStackTrace(); 98 | return false; 99 | } finally { 100 | if (database != null) { 101 | database.close(); 102 | } 103 | application.deleteDatabase(encryptedDbName); 104 | SQLiteDatabase.releaseMemory(); 105 | } 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/AndroidCompiledStatement.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android; 2 | 3 | 4 | import com.j256.ormlite.dao.ObjectCache; 5 | import com.j256.ormlite.field.SqlType; 6 | import com.j256.ormlite.logger.Logger; 7 | import com.j256.ormlite.logger.LoggerFactory; 8 | import com.j256.ormlite.misc.SqlExceptionUtil; 9 | import com.j256.ormlite.sqlcipher.android.compat.ApiCompatibility; 10 | import com.j256.ormlite.sqlcipher.android.compat.ApiCompatibilityUtils; 11 | import com.j256.ormlite.stmt.StatementBuilder.StatementType; 12 | import com.j256.ormlite.support.CompiledStatement; 13 | import com.j256.ormlite.support.DatabaseResults; 14 | 15 | import net.sqlcipher.Cursor; 16 | import net.sqlcipher.database.SQLiteDatabase; 17 | import net.sqlcipher.database.SQLiteStatement; 18 | 19 | import java.sql.SQLException; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * Android implementation of the compiled statement. 25 | * 26 | * @author kevingalligan, graywatson 27 | */ 28 | public class AndroidCompiledStatement implements CompiledStatement { 29 | 30 | private static Logger logger = LoggerFactory.getLogger(AndroidCompiledStatement.class); 31 | 32 | private static final String[] NO_STRING_ARGS = new String[0]; 33 | private static final ApiCompatibility apiCompatibility = ApiCompatibilityUtils.getCompatibility(); 34 | 35 | private final String sql; 36 | private final SQLiteDatabase db; 37 | private final StatementType type; 38 | private final boolean cancelQueriesEnabled; 39 | 40 | private Cursor cursor; 41 | private List args; 42 | private Integer max; 43 | private ApiCompatibility.CancellationHook cancellationHook; 44 | 45 | public AndroidCompiledStatement(String sql, SQLiteDatabase db, StatementType type, boolean cancelQueriesEnabled) { 46 | this.sql = sql; 47 | this.db = db; 48 | this.type = type; 49 | this.cancelQueriesEnabled = cancelQueriesEnabled; 50 | } 51 | 52 | public int getColumnCount() throws SQLException { 53 | return getCursor().getColumnCount(); 54 | } 55 | 56 | public String getColumnName(int column) throws SQLException { 57 | return getCursor().getColumnName(column); 58 | } 59 | 60 | public DatabaseResults runQuery(ObjectCache objectCache) throws SQLException { 61 | // this could come from DELETE or UPDATE, just not a SELECT 62 | if (!type.isOkForQuery()) { 63 | throw new IllegalArgumentException("Cannot call query on a " + type + " statement"); 64 | } 65 | return new AndroidDatabaseResults(getCursor(), objectCache); 66 | } 67 | 68 | public int runUpdate() throws SQLException { 69 | if (!type.isOkForUpdate()) { 70 | throw new IllegalArgumentException("Cannot call update on a " + type + " statement"); 71 | } 72 | String finalSql; 73 | if (max == null) { 74 | finalSql = sql; 75 | } else { 76 | finalSql = sql + " " + max; 77 | } 78 | return execSql(db, "runUpdate", finalSql, getArgArray()); 79 | } 80 | 81 | public int runExecute() throws SQLException { 82 | if (!type.isOkForExecute()) { 83 | throw new IllegalArgumentException("Cannot call execute on a " + type + " statement"); 84 | } 85 | return execSql(db, "runExecute", sql, getArgArray()); 86 | } 87 | 88 | public void close() throws SQLException { 89 | if (cursor != null) { 90 | try { 91 | cursor.close(); 92 | } catch (android.database.SQLException e) { 93 | throw SqlExceptionUtil.create("Problems closing Android cursor", e); 94 | } 95 | } 96 | cancellationHook = null; 97 | } 98 | 99 | public void closeQuietly() { 100 | try { 101 | close(); 102 | } catch (SQLException e) { 103 | // ignored 104 | } 105 | } 106 | 107 | public void cancel() { 108 | if (cancellationHook != null) { 109 | cancellationHook.cancel(); 110 | } 111 | } 112 | 113 | public void setObject(int parameterIndex, Object obj, SqlType sqlType) throws SQLException { 114 | isInPrep(); 115 | if (args == null) { 116 | args = new ArrayList(); 117 | } 118 | if (obj == null) { 119 | args.add(parameterIndex, null); 120 | return; 121 | } 122 | 123 | switch (sqlType) { 124 | case STRING : 125 | case LONG_STRING : 126 | case DATE : 127 | case BOOLEAN : 128 | case CHAR : 129 | case BYTE : 130 | case SHORT : 131 | case INTEGER : 132 | case LONG : 133 | case FLOAT : 134 | case DOUBLE : 135 | args.add(parameterIndex, obj.toString()); 136 | break; 137 | case BYTE_ARRAY : 138 | case SERIALIZABLE : 139 | args.add(parameterIndex, obj); 140 | break; 141 | case BLOB : 142 | // this is only for derby serializable 143 | case BIG_DECIMAL : 144 | // this should be handled as a STRING 145 | throw new SQLException("Invalid Android type: " + sqlType); 146 | case UNKNOWN : 147 | default : 148 | throw new SQLException("Unknown sql argument type: " + sqlType); 149 | } 150 | } 151 | 152 | public void setMaxRows(int max) throws SQLException { 153 | isInPrep(); 154 | this.max = max; 155 | } 156 | 157 | public void setQueryTimeout(long millis) { 158 | // as far as I could tell this is not supported by Android API 159 | } 160 | 161 | /*** 162 | * This is mostly an internal class but is exposed for those people who need access to the Cursor itself. 163 | * 164 | *

165 | * NOTE: This is not thread safe. Not sure if we need it, but keep that in mind. 166 | *

167 | */ 168 | public Cursor getCursor() throws SQLException { 169 | if (cursor == null) { 170 | String finalSql = null; 171 | try { 172 | if (max == null) { 173 | finalSql = sql; 174 | } else { 175 | finalSql = sql + " " + max; 176 | } 177 | if (cancelQueriesEnabled) { 178 | cancellationHook = apiCompatibility.createCancellationHook(); 179 | } 180 | cursor = apiCompatibility.rawQuery(db, finalSql, getStringArray(), cancellationHook); 181 | cursor.moveToFirst(); 182 | logger.trace("{}: started rawQuery cursor for: {}", this, finalSql); 183 | } catch (android.database.SQLException e) { 184 | throw SqlExceptionUtil.create("Problems executing Android query: " + finalSql, e); 185 | } 186 | } 187 | 188 | return cursor; 189 | } 190 | 191 | @Override 192 | public String toString() { 193 | return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode()); 194 | } 195 | 196 | /** 197 | * Execute some SQL on the database and return the number of rows changed. 198 | */ 199 | static int execSql(SQLiteDatabase db, String label, String finalSql, Object[] argArray) throws SQLException { 200 | try { 201 | db.execSQL(finalSql, argArray); 202 | } catch (android.database.SQLException e) { 203 | throw SqlExceptionUtil.create("Problems executing " + label + " Android statement: " + finalSql, e); 204 | } 205 | int result; 206 | SQLiteStatement stmt = null; 207 | try { 208 | // ask sqlite how many rows were just changed 209 | stmt = db.compileStatement("SELECT CHANGES()"); 210 | result = (int) stmt.simpleQueryForLong(); 211 | } catch (android.database.SQLException e) { 212 | // ignore the exception and just return 1 if it failed 213 | result = 1; 214 | } finally { 215 | if (stmt != null) { 216 | stmt.close(); 217 | } 218 | } 219 | logger.trace("executing statement {} changed {} rows: {}", label, result, finalSql); 220 | return result; 221 | } 222 | 223 | private void isInPrep() throws SQLException { 224 | if (cursor != null) { 225 | throw new SQLException("Query already run. Cannot add argument values."); 226 | } 227 | } 228 | 229 | private Object[] getArgArray() { 230 | if (args == null) { 231 | // this will work for Object[] as well as String[] 232 | return NO_STRING_ARGS; 233 | } else { 234 | return args.toArray(new Object[args.size()]); 235 | } 236 | } 237 | 238 | private String[] getStringArray() { 239 | if (args == null) { 240 | return NO_STRING_ARGS; 241 | } else { 242 | // we assume we have Strings in args 243 | return args.toArray(new String[args.size()]); 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/AndroidConnectionSource.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android; 2 | 3 | 4 | import com.j256.ormlite.db.DatabaseType; 5 | import com.j256.ormlite.logger.Logger; 6 | import com.j256.ormlite.logger.LoggerFactory; 7 | import com.j256.ormlite.misc.SqlExceptionUtil; 8 | import com.j256.ormlite.sqlcipher.android.apptools.OrmLiteSqliteOpenHelper; 9 | import com.j256.ormlite.sqlcipher.db.SqliteAndroidDatabaseType; 10 | import com.j256.ormlite.support.BaseConnectionSource; 11 | import com.j256.ormlite.support.ConnectionSource; 12 | import com.j256.ormlite.support.DatabaseConnection; 13 | import com.j256.ormlite.support.DatabaseConnectionProxyFactory; 14 | 15 | import net.sqlcipher.database.SQLiteDatabase; 16 | import net.sqlcipher.database.SQLiteOpenHelper; 17 | 18 | import java.sql.SQLException; 19 | 20 | /** 21 | * Android version of the connection source. Takes a standard Android {@link SQLiteOpenHelper}. For best results, use 22 | * {@link OrmLiteSqliteOpenHelper}. You can also construct with a {@link SQLiteDatabase}. 23 | * 24 | * @author kevingalligan, graywatson 25 | */ 26 | public class AndroidConnectionSource extends BaseConnectionSource implements ConnectionSource { 27 | 28 | private static final Logger logger = LoggerFactory.getLogger(AndroidConnectionSource.class); 29 | 30 | private final SQLiteOpenHelper helper; 31 | private final SQLiteDatabase sqliteDatabase; 32 | private DatabaseConnection connection = null; 33 | private volatile boolean isOpen = true; 34 | private final DatabaseType databaseType = new SqliteAndroidDatabaseType(); 35 | private static DatabaseConnectionProxyFactory connectionProxyFactory; 36 | private boolean cancelQueriesEnabled = false; 37 | 38 | public AndroidConnectionSource(SQLiteOpenHelper helper) { 39 | this.helper = helper; 40 | this.sqliteDatabase = null; 41 | } 42 | 43 | public AndroidConnectionSource(SQLiteDatabase sqliteDatabase) { 44 | this.helper = null; 45 | this.sqliteDatabase = sqliteDatabase; 46 | } 47 | 48 | public DatabaseConnection getReadOnlyConnection() throws SQLException { 49 | /* 50 | * We have to use the read-write connection because getWritableDatabase() can call close on 51 | * getReadableDatabase() in the future. This has something to do with Android's SQLite connection management. 52 | * 53 | * See android docs: http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html 54 | */ 55 | return getReadWriteConnection(); 56 | } 57 | 58 | public DatabaseConnection getReadWriteConnection() throws SQLException { 59 | DatabaseConnection conn = getSavedConnection(); 60 | if (conn != null) { 61 | return conn; 62 | } 63 | if (connection == null) { 64 | SQLiteDatabase db; 65 | if (sqliteDatabase == null) { 66 | String password ; 67 | if (helper instanceof OrmLiteSqliteOpenHelper){ 68 | password = ((OrmLiteSqliteOpenHelper)helper).getCipherPassword(); 69 | }else { 70 | throw new IllegalArgumentException("you should set sqlcipher password to open database"); 71 | } 72 | try { 73 | db = helper.getWritableDatabase(password); 74 | } catch (android.database.SQLException e) { 75 | throw SqlExceptionUtil.create("Getting a writable database from helper " + helper + " failed", e); 76 | } 77 | } else { 78 | db = sqliteDatabase; 79 | } 80 | connection = new AndroidDatabaseConnection(db, true, cancelQueriesEnabled); 81 | if (connectionProxyFactory != null) { 82 | connection = connectionProxyFactory.createProxy(connection); 83 | } 84 | logger.trace("created connection {} for db {}, helper {}", connection, db, helper); 85 | } else { 86 | logger.trace("{}: returning read-write connection {}, helper {}", this, connection, helper); 87 | } 88 | return connection; 89 | } 90 | 91 | public void releaseConnection(DatabaseConnection connection) { 92 | // noop since connection management is handled by AndroidOS 93 | } 94 | 95 | public boolean saveSpecialConnection(DatabaseConnection connection) throws SQLException { 96 | return saveSpecial(connection); 97 | } 98 | 99 | public void clearSpecialConnection(DatabaseConnection connection) { 100 | clearSpecial(connection, logger); 101 | } 102 | 103 | public void close() { 104 | // the helper is closed so it calls close here, so this CANNOT be a call back to helper.close() 105 | isOpen = false; 106 | } 107 | 108 | public void closeQuietly() { 109 | close(); 110 | } 111 | 112 | public DatabaseType getDatabaseType() { 113 | return databaseType; 114 | } 115 | 116 | public boolean isOpen() { 117 | return isOpen; 118 | } 119 | 120 | /** 121 | * Set to enable connection proxying. Set to null to disable. 122 | */ 123 | public static void setDatabaseConnectionProxyFactory(DatabaseConnectionProxyFactory connectionProxyFactory) { 124 | AndroidConnectionSource.connectionProxyFactory = connectionProxyFactory; 125 | } 126 | 127 | public boolean isCancelQueriesEnabled() { 128 | return cancelQueriesEnabled; 129 | } 130 | 131 | /** 132 | * Set to true to enable the canceling of queries. 133 | * 134 | *

135 | * NOTE: This will incur a slight memory increase for all Cursor based queries -- even if cancel is not 136 | * called for them. 137 | *

138 | */ 139 | public void setCancelQueriesEnabled(boolean cancelQueriesEnabled) { 140 | this.cancelQueriesEnabled = cancelQueriesEnabled; 141 | } 142 | 143 | @Override 144 | public String toString() { 145 | return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode()); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/AndroidDatabaseResults.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android; 2 | 3 | 4 | import com.j256.ormlite.dao.ObjectCache; 5 | import com.j256.ormlite.db.DatabaseType; 6 | import com.j256.ormlite.sqlcipher.db.SqliteAndroidDatabaseType; 7 | import com.j256.ormlite.support.DatabaseResults; 8 | 9 | import net.sqlcipher.Cursor; 10 | 11 | import java.io.ByteArrayInputStream; 12 | import java.io.InputStream; 13 | import java.math.BigDecimal; 14 | import java.sql.SQLException; 15 | import java.sql.Timestamp; 16 | import java.util.Arrays; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | /** 21 | * Android implementation of our results object. 22 | * 23 | * @author kevingalligan, graywatson 24 | */ 25 | public class AndroidDatabaseResults implements DatabaseResults { 26 | 27 | private static final int MIN_NUM_COLUMN_NAMES_MAP = 8; 28 | 29 | private final Cursor cursor; 30 | private final String[] columnNames; 31 | private final Map columnNameMap; 32 | private final ObjectCache objectCache; 33 | private static final DatabaseType databaseType = new SqliteAndroidDatabaseType(); 34 | 35 | public AndroidDatabaseResults(Cursor cursor, ObjectCache objectCache) { 36 | this.cursor = cursor; 37 | this.columnNames = cursor.getColumnNames(); 38 | if (this.columnNames.length >= MIN_NUM_COLUMN_NAMES_MAP) { 39 | this.columnNameMap = new HashMap(); 40 | for (int i = 0; i < this.columnNames.length; i++) { 41 | // NOTE: this is case sensitive 42 | this.columnNameMap.put(this.columnNames[i], i); 43 | } 44 | } else { 45 | columnNameMap = null; 46 | } 47 | this.objectCache = objectCache; 48 | } 49 | 50 | /** 51 | * Constructor that allows you to inject a cursor that has already been configured with first-call set to false. 52 | * 53 | * @deprecated The firstCall is no longer needed since the library now calls first() and next on its own. 54 | */ 55 | @Deprecated 56 | public AndroidDatabaseResults(Cursor cursor, boolean firstCall, ObjectCache objectCache) { 57 | this(cursor, objectCache); 58 | } 59 | 60 | public int getColumnCount() { 61 | return cursor.getColumnCount(); 62 | } 63 | 64 | public String[] getColumnNames() { 65 | int colN = getColumnCount(); 66 | String[] columnNames = new String[colN]; 67 | for (int colC = 0; colC < colN; colC++) { 68 | columnNames[colC] = cursor.getColumnName(colC); 69 | } 70 | return columnNames; 71 | } 72 | 73 | public boolean first() { 74 | return cursor.moveToFirst(); 75 | } 76 | 77 | public boolean next() { 78 | return cursor.moveToNext(); 79 | } 80 | 81 | public boolean last() { 82 | return cursor.moveToLast(); 83 | } 84 | 85 | public boolean previous() { 86 | return cursor.moveToPrevious(); 87 | } 88 | 89 | public boolean moveRelative(int offset) { 90 | return cursor.move(offset); 91 | } 92 | 93 | public boolean moveAbsolute(int position) { 94 | return cursor.moveToPosition(position); 95 | } 96 | 97 | /** 98 | * Returns the count of results from the cursor. 99 | */ 100 | public int getCount() { 101 | return cursor.getCount(); 102 | } 103 | 104 | /** 105 | * Returns the position of the cursor in the list of results. 106 | */ 107 | public int getPosition() { 108 | return cursor.getPosition(); 109 | } 110 | 111 | public int findColumn(String columnName) throws SQLException { 112 | int index = lookupColumn(columnName); 113 | if (index >= 0) { 114 | return index; 115 | } 116 | 117 | /* 118 | * Hack here. It turns out that if we've asked for '*' then the field foo is in the cursor as foo. But if we ask 119 | * for a particular field list with DISTINCT, which escapes the field names, they are in the cursor _with_ the 120 | * escaping. Ugly!! 121 | */ 122 | StringBuilder sb = new StringBuilder(columnName.length() + 4); 123 | databaseType.appendEscapedEntityName(sb, columnName); 124 | index = lookupColumn(sb.toString()); 125 | if (index >= 0) { 126 | return index; 127 | } else { 128 | String[] columnNames = cursor.getColumnNames(); 129 | throw new SQLException("Unknown field '" + columnName + "' from the Android sqlite cursor, not in:" 130 | + Arrays.toString(columnNames)); 131 | } 132 | } 133 | 134 | public String getString(int columnIndex) { 135 | return cursor.getString(columnIndex); 136 | } 137 | 138 | public boolean getBoolean(int columnIndex) { 139 | if (cursor.isNull(columnIndex) || cursor.getShort(columnIndex) == 0) { 140 | return false; 141 | } else { 142 | return true; 143 | } 144 | } 145 | 146 | public char getChar(int columnIndex) throws SQLException { 147 | String string = cursor.getString(columnIndex); 148 | if (string == null || string.length() == 0) { 149 | return 0; 150 | } else if (string.length() == 1) { 151 | return string.charAt(0); 152 | } else { 153 | throw new SQLException("More than 1 character stored in database column: " + columnIndex); 154 | } 155 | } 156 | 157 | public byte getByte(int columnIndex) { 158 | return (byte) getShort(columnIndex); 159 | } 160 | 161 | public byte[] getBytes(int columnIndex) { 162 | return cursor.getBlob(columnIndex); 163 | } 164 | 165 | public short getShort(int columnIndex) { 166 | return cursor.getShort(columnIndex); 167 | } 168 | 169 | public int getInt(int columnIndex) { 170 | return cursor.getInt(columnIndex); 171 | } 172 | 173 | public long getLong(int columnIndex) { 174 | return cursor.getLong(columnIndex); 175 | } 176 | 177 | public float getFloat(int columnIndex) { 178 | return cursor.getFloat(columnIndex); 179 | } 180 | 181 | public double getDouble(int columnIndex) { 182 | return cursor.getDouble(columnIndex); 183 | } 184 | 185 | public Timestamp getTimestamp(int columnIndex) throws SQLException { 186 | throw new SQLException("Android does not support timestamp. Use JAVA_DATE_LONG or JAVA_DATE_STRING types"); 187 | } 188 | 189 | public InputStream getBlobStream(int columnIndex) { 190 | return new ByteArrayInputStream(cursor.getBlob(columnIndex)); 191 | } 192 | 193 | public BigDecimal getBigDecimal(int columnIndex) throws SQLException { 194 | throw new SQLException("Android does not support BigDecimal type. Use BIG_DECIMAL or BIG_DECIMAL_STRING types"); 195 | } 196 | 197 | public boolean wasNull(int columnIndex) { 198 | return cursor.isNull(columnIndex); 199 | } 200 | 201 | public ObjectCache getObjectCache() { 202 | return objectCache; 203 | } 204 | 205 | public void close() { 206 | cursor.close(); 207 | } 208 | 209 | public void closeQuietly() { 210 | close(); 211 | } 212 | 213 | /*** 214 | * Returns the underlying Android cursor object. This should not be used unless you know what you are doing. 215 | */ 216 | public Cursor getRawCursor() { 217 | return cursor; 218 | } 219 | 220 | @Override 221 | public String toString() { 222 | return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode()); 223 | } 224 | 225 | private int lookupColumn(String columnName) { 226 | // we either use linear search or our name map 227 | if (columnNameMap == null) { 228 | for (int i = 0; i < columnNames.length; i++) { 229 | // NOTE: this is case sensitive 230 | if (columnNames[i].equals(columnName)) { 231 | return i; 232 | } 233 | } 234 | return -1; 235 | } else { 236 | // NOTE: this is case sensitive 237 | Integer index = columnNameMap.get(columnName); 238 | if (index == null) { 239 | return -1; 240 | } else { 241 | return index; 242 | } 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/AndroidLog.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android; 2 | 3 | import android.util.Log; 4 | 5 | import com.j256.ormlite.logger.LoggerFactory; 6 | 7 | /** 8 | * Implementation of our logger which delegates to the internal Android logger. 9 | * 10 | *

11 | * To see log messages you will do something like: 12 | * 13 | *

 14 |  * adb shell setprop log.tag.OrmLiteBaseActivity VERBOSE
 15 |  * 
16 | * 17 | *

18 | * 19 | *

20 | * NOTE: Unfortunately, Android variables are limited in size so this class takes that last 23 (sic) characters 21 | * of the class name if it is larger than 23 characters. For example, if the class is AndroidDatabaseConnection you 22 | * would do: 23 | * 24 | *

 25 |  * adb shell setprop log.tag.droidDatabaseConnection VERBOSE
 26 |  * 
27 | * 28 | *

29 | * 30 | *

31 | * To see all ORMLite log messages use: 32 | * 33 | *

 34 |  * adb shell setprop log.tag.ORMLite DEBUG
 35 |  * 
36 | * 37 | * @author graywatson 38 | */ 39 | public class AndroidLog implements com.j256.ormlite.logger.Log { 40 | 41 | private final static String ALL_LOGS_NAME = "ORMLite"; 42 | private final static int REFRESH_LEVEL_CACHE_EVERY = 200; 43 | 44 | private final static int MAX_TAG_LENGTH = 23; 45 | private String className; 46 | 47 | // we do this because supposedly Log.isLoggable() always does IO 48 | private volatile int levelCacheC = 0; 49 | private final boolean levelCache[]; 50 | 51 | public AndroidLog(String className) { 52 | // get the last part of the class name 53 | this.className = LoggerFactory.getSimpleClassName(className); 54 | // make sure that our tag length is not too long 55 | int length = this.className.length(); 56 | if (length > MAX_TAG_LENGTH) { 57 | this.className = this.className.substring(length - MAX_TAG_LENGTH, length); 58 | } 59 | // find the maximum level value 60 | int maxLevel = 0; 61 | for (com.j256.ormlite.logger.Log.Level level : com.j256.ormlite.logger.Log.Level.values()) { 62 | int androidLevel = levelToAndroidLevel(level); 63 | if (androidLevel > maxLevel) { 64 | maxLevel = androidLevel; 65 | } 66 | } 67 | levelCache = new boolean[maxLevel + 1]; 68 | refreshLevelCache(); 69 | } 70 | 71 | public boolean isLevelEnabled(Level level) { 72 | // we don't care if this is not synchronized, it will be updated sooner or later and multiple updates are fine. 73 | if (++levelCacheC >= REFRESH_LEVEL_CACHE_EVERY) { 74 | refreshLevelCache(); 75 | levelCacheC = 0; 76 | } 77 | int androidLevel = levelToAndroidLevel(level); 78 | if (androidLevel < levelCache.length) { 79 | return levelCache[androidLevel]; 80 | } else { 81 | return isLevelEnabledInternal(androidLevel); 82 | } 83 | } 84 | 85 | public void log(Level level, String msg) { 86 | switch (level) { 87 | case TRACE : 88 | Log.v(className, msg); 89 | break; 90 | case DEBUG : 91 | Log.d(className, msg); 92 | break; 93 | case INFO : 94 | Log.i(className, msg); 95 | break; 96 | case WARNING : 97 | Log.w(className, msg); 98 | break; 99 | case ERROR : 100 | Log.e(className, msg); 101 | break; 102 | case FATAL : 103 | Log.e(className, msg); 104 | break; 105 | default : 106 | Log.i(className, msg); 107 | break; 108 | } 109 | } 110 | 111 | public void log(Level level, String msg, Throwable t) { 112 | switch (level) { 113 | case TRACE : 114 | Log.v(className, msg, t); 115 | break; 116 | case DEBUG : 117 | Log.d(className, msg, t); 118 | break; 119 | case INFO : 120 | Log.i(className, msg, t); 121 | break; 122 | case WARNING : 123 | Log.w(className, msg, t); 124 | break; 125 | case ERROR : 126 | Log.e(className, msg, t); 127 | break; 128 | case FATAL : 129 | Log.e(className, msg, t); 130 | break; 131 | default : 132 | Log.i(className, msg, t); 133 | break; 134 | } 135 | } 136 | 137 | private void refreshLevelCache() { 138 | for (com.j256.ormlite.logger.Log.Level level : com.j256.ormlite.logger.Log.Level.values()) { 139 | int androidLevel = levelToAndroidLevel(level); 140 | if (androidLevel < levelCache.length) { 141 | levelCache[androidLevel] = isLevelEnabledInternal(androidLevel); 142 | } 143 | } 144 | } 145 | 146 | private boolean isLevelEnabledInternal(int androidLevel) { 147 | // this is supposedly expensive with an IO operation for each call so we cache them into levelCache[] 148 | return Log.isLoggable(className, androidLevel) || Log.isLoggable(ALL_LOGS_NAME, androidLevel); 149 | } 150 | 151 | private int levelToAndroidLevel(com.j256.ormlite.logger.Log.Level level) { 152 | switch (level) { 153 | case TRACE : 154 | return Log.VERBOSE; 155 | case DEBUG : 156 | return Log.DEBUG; 157 | case INFO : 158 | return Log.INFO; 159 | case WARNING : 160 | return Log.WARN; 161 | case ERROR : 162 | return Log.ERROR; 163 | case FATAL : 164 | return Log.ERROR; 165 | default : 166 | return Log.INFO; 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteBaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | 7 | import com.j256.ormlite.logger.Logger; 8 | import com.j256.ormlite.logger.LoggerFactory; 9 | import com.j256.ormlite.support.ConnectionSource; 10 | 11 | /** 12 | * Base class to use for activities in Android. 13 | * 14 | * You can simply call {@link #getHelper()} to get your helper class, or {@link #getConnectionSource()} to get a 15 | * {@link ConnectionSource}. 16 | * 17 | * The method {@link #getHelper()} assumes you are using the default helper factory -- see {@link OpenHelperManager}. If 18 | * not, you'll need to provide your own helper instances which will need to implement a reference counting scheme. This 19 | * method will only be called if you use the database, and only called once for this activity's life-cycle. 'close' will 20 | * also be called once for each call to createInstance. 21 | * 22 | * @author graywatson, kevingalligan 23 | */ 24 | public abstract class OrmLiteBaseActivity extends Activity { 25 | 26 | private volatile H helper; 27 | private volatile boolean created = false; 28 | private volatile boolean destroyed = false; 29 | private static Logger logger = LoggerFactory.getLogger(OrmLiteBaseActivity.class); 30 | 31 | /** 32 | * Get a helper for this action. 33 | */ 34 | public H getHelper() { 35 | if (helper == null) { 36 | if (!created) { 37 | throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null"); 38 | } else if (destroyed) { 39 | throw new IllegalStateException( 40 | "A call to onDestroy has already been made and the helper cannot be used after that point"); 41 | } else { 42 | throw new IllegalStateException("Helper is null for some unknown reason"); 43 | } 44 | } else { 45 | return helper; 46 | } 47 | } 48 | 49 | /** 50 | * Get a connection source for this action. 51 | */ 52 | public ConnectionSource getConnectionSource() { 53 | return getHelper().getConnectionSource(); 54 | } 55 | 56 | @Override 57 | protected void onCreate(Bundle savedInstanceState) { 58 | if (helper == null) { 59 | helper = getHelperInternal(this); 60 | created = true; 61 | } 62 | super.onCreate(savedInstanceState); 63 | } 64 | 65 | @Override 66 | protected void onDestroy() { 67 | super.onDestroy(); 68 | releaseHelper(helper); 69 | destroyed = true; 70 | } 71 | 72 | /** 73 | * This is called internally by the class to populate the helper object instance. This should not be called directly 74 | * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are 75 | * managing your own helper creation, override this method to supply this activity with a helper instance. 76 | * 77 | *

78 | * NOTE: If you override this method, you most likely will need to override the 79 | * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well. 80 | *

81 | */ 82 | protected H getHelperInternal(Context context) { 83 | @SuppressWarnings({ "unchecked", "deprecation" }) 84 | H newHelper = (H) OpenHelperManager.getHelper(context); 85 | logger.trace("{}: got new helper {} from OpenHelperManager", this, newHelper); 86 | return newHelper; 87 | } 88 | 89 | /** 90 | * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call 91 | * this directly since {@link #onDestroy()} does it for you. 92 | * 93 | *

94 | * NOTE: If you override this method, you most likely will need to override the 95 | * {@link #getHelperInternal(Context)} method as well. 96 | *

97 | */ 98 | protected void releaseHelper(H helper) { 99 | OpenHelperManager.releaseHelper(); 100 | logger.trace("{}: helper {} was released, set to null", this, helper); 101 | this.helper = null; 102 | } 103 | 104 | @Override 105 | public String toString() { 106 | return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode()); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteBaseActivityGroup.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.app.ActivityGroup; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | 7 | import com.j256.ormlite.support.ConnectionSource; 8 | 9 | /** 10 | * Base class to use for activity groups in Android. 11 | * 12 | * You can simply call {@link #getHelper()} to get your helper class, or {@link #getConnectionSource()} to get a 13 | * {@link ConnectionSource}. 14 | * 15 | * The method {@link #getHelper()} assumes you are using the default helper factory -- see {@link OpenHelperManager}. If 16 | * not, you'll need to provide your own helper instances which will need to implement a reference counting scheme. This 17 | * method will only be called if you use the database, and only called once for this activity's life-cycle. 'close' will 18 | * also be called once for each call to createInstance. 19 | * 20 | * @author graywatson, kevingalligan 21 | */ 22 | public abstract class OrmLiteBaseActivityGroup extends ActivityGroup { 23 | 24 | private volatile H helper; 25 | private volatile boolean created = false; 26 | private volatile boolean destroyed = false; 27 | 28 | /** 29 | * Get a helper for this action. 30 | */ 31 | public H getHelper() { 32 | if (helper == null) { 33 | if (!created) { 34 | throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null"); 35 | } else if (destroyed) { 36 | throw new IllegalStateException( 37 | "A call to onDestroy has already been made and the helper cannot be used after that point"); 38 | } else { 39 | throw new IllegalStateException("Helper is null for some unknown reason"); 40 | } 41 | } else { 42 | return helper; 43 | } 44 | } 45 | 46 | /** 47 | * Get a connection source for this action. 48 | */ 49 | public ConnectionSource getConnectionSource() { 50 | return getHelper().getConnectionSource(); 51 | } 52 | 53 | @Override 54 | protected void onCreate(Bundle savedInstanceState) { 55 | if (helper == null) { 56 | helper = getHelperInternal(this); 57 | created = true; 58 | } 59 | super.onCreate(savedInstanceState); 60 | } 61 | 62 | @Override 63 | protected void onDestroy() { 64 | super.onDestroy(); 65 | releaseHelper(helper); 66 | destroyed = true; 67 | } 68 | 69 | /** 70 | * This is called internally by the class to populate the helper object instance. This should not be called directly 71 | * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are 72 | * managing your own helper creation, override this method to supply this activity with a helper instance. 73 | * 74 | *

75 | * NOTE: If you override this method, you most likely will need to override the 76 | * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well. 77 | *

78 | */ 79 | protected H getHelperInternal(Context context) { 80 | @SuppressWarnings({ "unchecked", "deprecation" }) 81 | H newHelper = (H) OpenHelperManager.getHelper(context); 82 | return newHelper; 83 | } 84 | 85 | /** 86 | * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call 87 | * this directly since {@link #onDestroy()} does it for you. 88 | * 89 | *

90 | * NOTE: If you override this method, you most likely will need to override the 91 | * {@link #getHelperInternal(Context)} method as well. 92 | *

93 | */ 94 | protected void releaseHelper(H helper) { 95 | OpenHelperManager.releaseHelper(); 96 | this.helper = null; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteBaseListActivity.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.app.ListActivity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | 7 | import com.j256.ormlite.support.ConnectionSource; 8 | 9 | /** 10 | * Base class to use for Tab activities in Android. 11 | * 12 | * For more information, see {@link OrmLiteBaseActivity}. 13 | * 14 | * @author graywatson, kevingalligan 15 | */ 16 | public abstract class OrmLiteBaseListActivity extends ListActivity { 17 | 18 | private volatile H helper; 19 | private volatile boolean created = false; 20 | private volatile boolean destroyed = false; 21 | 22 | /** 23 | * Get a helper for this action. 24 | */ 25 | public H getHelper() { 26 | if (helper == null) { 27 | if (!created) { 28 | throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null"); 29 | } else if (destroyed) { 30 | throw new IllegalStateException( 31 | "A call to onDestroy has already been made and the helper cannot be used after that point"); 32 | } else { 33 | throw new IllegalStateException("Helper is null for some unknown reason"); 34 | } 35 | } else { 36 | return helper; 37 | } 38 | } 39 | 40 | /** 41 | * Get a connection source for this action. 42 | */ 43 | public ConnectionSource getConnectionSource() { 44 | return getHelper().getConnectionSource(); 45 | } 46 | 47 | @Override 48 | protected void onCreate(Bundle savedInstanceState) { 49 | if (helper == null) { 50 | helper = getHelperInternal(this); 51 | created = true; 52 | } 53 | super.onCreate(savedInstanceState); 54 | } 55 | 56 | @Override 57 | protected void onDestroy() { 58 | super.onDestroy(); 59 | releaseHelper(helper); 60 | destroyed = true; 61 | } 62 | 63 | /** 64 | * This is called internally by the class to populate the helper object instance. This should not be called directly 65 | * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are 66 | * managing your own helper creation, override this method to supply this activity with a helper instance. 67 | * 68 | *

69 | * NOTE: If you override this method, you most likely will need to override the 70 | * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well. 71 | *

72 | */ 73 | protected H getHelperInternal(Context context) { 74 | @SuppressWarnings({ "unchecked", "deprecation" }) 75 | H newHelper = (H) OpenHelperManager.getHelper(context); 76 | return newHelper; 77 | } 78 | 79 | /** 80 | * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call 81 | * this directly since {@link #onDestroy()} does it for you. 82 | * 83 | *

84 | * NOTE: If you override this method, you most likely will need to override the 85 | * {@link #getHelperInternal(Context)} method as well. 86 | *

87 | */ 88 | protected void releaseHelper(H helper) { 89 | OpenHelperManager.releaseHelper(); 90 | this.helper = null; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteBaseService.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.app.Service; 4 | import android.content.Context; 5 | 6 | import com.j256.ormlite.support.ConnectionSource; 7 | 8 | /** 9 | * Base class to use for services in Android. 10 | * 11 | * For more information, see {@link OrmLiteBaseActivity}. 12 | * 13 | * @author graywatson, kevingalligan 14 | */ 15 | public abstract class OrmLiteBaseService extends Service { 16 | 17 | private volatile H helper; 18 | private volatile boolean created = false; 19 | private volatile boolean destroyed = false; 20 | 21 | /** 22 | * Get a helper for this action. 23 | */ 24 | public H getHelper() { 25 | if (helper == null) { 26 | if (!created) { 27 | throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null"); 28 | } else if (destroyed) { 29 | throw new IllegalStateException( 30 | "A call to onDestroy has already been made and the helper cannot be used after that point"); 31 | } else { 32 | throw new IllegalStateException("Helper is null for some unknown reason"); 33 | } 34 | } else { 35 | return helper; 36 | } 37 | } 38 | 39 | /** 40 | * Get a connection source for this action. 41 | */ 42 | public ConnectionSource getConnectionSource() { 43 | return getHelper().getConnectionSource(); 44 | } 45 | 46 | @Override 47 | public void onCreate() { 48 | if (helper == null) { 49 | helper = getHelperInternal(this); 50 | created = true; 51 | } 52 | super.onCreate(); 53 | } 54 | 55 | @Override 56 | public void onDestroy() { 57 | super.onDestroy(); 58 | releaseHelper(helper); 59 | destroyed = true; 60 | } 61 | 62 | /** 63 | * This is called internally by the class to populate the helper object instance. This should not be called directly 64 | * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are 65 | * managing your own helper creation, override this method to supply this activity with a helper instance. 66 | * 67 | *

68 | * NOTE: If you override this method, you most likely will need to override the 69 | * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well. 70 | *

71 | */ 72 | protected H getHelperInternal(Context context) { 73 | @SuppressWarnings({ "unchecked", "deprecation" }) 74 | H newHelper = (H) OpenHelperManager.getHelper(context); 75 | return newHelper; 76 | } 77 | 78 | /** 79 | * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call 80 | * this directly since {@link #onDestroy()} does it for you. 81 | * 82 | *

83 | * NOTE: If you override this method, you most likely will need to override the 84 | * {@link #getHelperInternal(Context)} method as well. 85 | *

86 | */ 87 | protected void releaseHelper(H helper) { 88 | OpenHelperManager.releaseHelper(); 89 | this.helper = null; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteBaseTabActivity.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import android.app.TabActivity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | 7 | import com.j256.ormlite.support.ConnectionSource; 8 | 9 | /** 10 | * Base class to use for Tab activities in Android. 11 | * 12 | * For more information, see {@link OrmLiteBaseActivity}. 13 | * 14 | * @author graywatson, kevingalligan 15 | */ 16 | public abstract class OrmLiteBaseTabActivity extends TabActivity { 17 | 18 | private volatile H helper; 19 | private volatile boolean created = false; 20 | private volatile boolean destroyed = false; 21 | 22 | /** 23 | * Get a helper for this action. 24 | */ 25 | public H getHelper() { 26 | if (helper == null) { 27 | if (!created) { 28 | throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null"); 29 | } else if (destroyed) { 30 | throw new IllegalStateException( 31 | "A call to onDestroy has already been made and the helper cannot be used after that point"); 32 | } else { 33 | throw new IllegalStateException("Helper is null for some unknown reason"); 34 | } 35 | } else { 36 | return helper; 37 | } 38 | } 39 | 40 | /** 41 | * Get a connection source for this action. 42 | */ 43 | public ConnectionSource getConnectionSource() { 44 | return getHelper().getConnectionSource(); 45 | } 46 | 47 | @Override 48 | protected void onCreate(Bundle savedInstanceState) { 49 | if (helper == null) { 50 | helper = getHelperInternal(this); 51 | created = true; 52 | } 53 | super.onCreate(savedInstanceState); 54 | } 55 | 56 | @Override 57 | protected void onDestroy() { 58 | super.onDestroy(); 59 | releaseHelper(helper); 60 | destroyed = true; 61 | } 62 | 63 | /** 64 | * This is called internally by the class to populate the helper object instance. This should not be called directly 65 | * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are 66 | * managing your own helper creation, override this method to supply this activity with a helper instance. 67 | * 68 | *

69 | * NOTE: If you override this method, you most likely will need to override the 70 | * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well. 71 | *

72 | * 73 | * @see OpenHelperManager#getHelper(Context) 74 | */ 75 | protected H getHelperInternal(Context context) { 76 | @SuppressWarnings({ "unchecked", "deprecation" }) 77 | H newHelper = (H) OpenHelperManager.getHelper(context); 78 | return newHelper; 79 | } 80 | 81 | /** 82 | * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call 83 | * this directly since {@link #onDestroy()} does it for you. 84 | * 85 | *

86 | * NOTE: If you override this method, you most likely will need to override the 87 | * {@link #getHelperInternal(Context)} method as well. 88 | *

89 | */ 90 | protected void releaseHelper(H helper) { 91 | OpenHelperManager.releaseHelper(); 92 | this.helper = null; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/apptools/OrmLiteConfigUtil.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.apptools; 2 | 3 | import com.j256.ormlite.dao.DaoManager; 4 | import com.j256.ormlite.db.DatabaseType; 5 | import com.j256.ormlite.field.DatabaseField; 6 | import com.j256.ormlite.field.DatabaseFieldConfig; 7 | import com.j256.ormlite.field.ForeignCollectionField; 8 | import com.j256.ormlite.sqlcipher.db.SqliteAndroidDatabaseType; 9 | import com.j256.ormlite.table.DatabaseTable; 10 | import com.j256.ormlite.table.DatabaseTableConfig; 11 | import com.j256.ormlite.table.DatabaseTableConfigLoader; 12 | 13 | import net.sqlcipher.database.SQLiteDatabase; 14 | 15 | import java.io.BufferedReader; 16 | import java.io.BufferedWriter; 17 | import java.io.File; 18 | import java.io.FileFilter; 19 | import java.io.FileOutputStream; 20 | import java.io.FileReader; 21 | import java.io.IOException; 22 | import java.io.OutputStream; 23 | import java.io.OutputStreamWriter; 24 | import java.lang.reflect.Field; 25 | import java.sql.SQLException; 26 | import java.text.SimpleDateFormat; 27 | import java.util.ArrayList; 28 | import java.util.Date; 29 | import java.util.List; 30 | 31 | /** 32 | * Database configuration file helper class that is used to write a configuration file into the raw resource 33 | * sub-directory to speed up DAO creation. 34 | * 35 | *

36 | * With help from the user list and especially Ian Dees, we discovered that calls to annotation methods in Android are 37 | * _very_ expensive because Method.equals() was doing a huge toString(). This was causing folks to see 2-3 seconds 38 | * startup time when configuring 10-15 DAOs because of 1000s of calls to @DatabaseField methods. See this Android bug report. 40 | *

41 | * 42 | *

43 | * I added this utility class which writes a configuration file into the raw resource "res/raw" directory inside of your 44 | * project containing the table and field names and associated details. This file can then be loaded into the 45 | * {@link DaoManager} with the help of the 46 | * {@link OrmLiteSqliteOpenHelper#OrmLiteSqliteOpenHelper(android.content.Context, String, SQLiteDatabase.CursorFactory, int, int)} 47 | * constructor. This means that you can configure your classes _without_ any runtime calls to annotations. It seems 48 | * significantly faster. 49 | *

50 | * 51 | *

52 | * WARNING: Although this is fast, the big problem is that you have to remember to regenerate the config file 53 | * whenever you edit one of your database classes. There is no way that I know of to do this automagically. 54 | *

55 | * 56 | * @author graywatson 57 | */ 58 | public class OrmLiteConfigUtil { 59 | 60 | /** 61 | * Resource directory name that we are looking for. 62 | */ 63 | protected static final String RESOURCE_DIR_NAME = "res"; 64 | /** 65 | * Raw directory name that we are looking for. 66 | */ 67 | protected static final String RAW_DIR_NAME = "raw"; 68 | 69 | /** 70 | * Maximum recursion level while we are looking for source files. 71 | */ 72 | protected static int maxFindSourceLevel = 20; 73 | 74 | private static final DatabaseType databaseType = new SqliteAndroidDatabaseType(); 75 | 76 | /** 77 | * A call through to {@link #writeConfigFile(String)} taking the file name from the single command line argument. 78 | */ 79 | public static void main(String[] args) throws Exception { 80 | if (args.length != 1) { 81 | throw new IllegalArgumentException("Main can take a single file-name argument."); 82 | } 83 | writeConfigFile(args[0]); 84 | } 85 | 86 | /** 87 | * Finds the annotated classes in the current directory or below and writes a configuration file to the file-name in 88 | * the raw folder. 89 | */ 90 | public static void writeConfigFile(String fileName) throws SQLException, IOException { 91 | List> classList = new ArrayList>(); 92 | findAnnotatedClasses(classList, new File("."), 0); 93 | writeConfigFile(fileName, classList.toArray(new Class[classList.size()])); 94 | } 95 | 96 | /** 97 | * Writes a configuration fileName in the raw directory with the configuration for classes. 98 | */ 99 | public static void writeConfigFile(String fileName, Class[] classes) throws SQLException, IOException { 100 | File rawDir = findRawDir(new File(".")); 101 | if (rawDir == null) { 102 | System.err.println("Could not find " + RAW_DIR_NAME + " directory which is typically in the " 103 | + RESOURCE_DIR_NAME + " directory"); 104 | } else { 105 | File configFile = new File(rawDir, fileName); 106 | writeConfigFile(configFile, classes); 107 | } 108 | } 109 | 110 | /** 111 | * Finds the annotated classes in the current directory or below and writes a configuration file. 112 | */ 113 | public static void writeConfigFile(File configFile) throws SQLException, IOException { 114 | writeConfigFile(configFile, new File(".")); 115 | } 116 | 117 | /** 118 | * Finds the annotated classes in the specified search directory or below and writes a configuration file. 119 | */ 120 | public static void writeConfigFile(File configFile, File searchDir) throws SQLException, IOException { 121 | List> classList = new ArrayList>(); 122 | findAnnotatedClasses(classList, searchDir, 0); 123 | writeConfigFile(configFile, classList.toArray(new Class[classList.size()])); 124 | } 125 | 126 | /** 127 | * Write a configuration file with the configuration for classes. 128 | */ 129 | public static void writeConfigFile(File configFile, Class[] classes) throws SQLException, IOException { 130 | System.out.println("Writing configurations to " + configFile.getAbsolutePath()); 131 | writeConfigFile(new FileOutputStream(configFile), classes); 132 | } 133 | 134 | /** 135 | * Write a configuration file to an output stream with the configuration for classes. 136 | */ 137 | public static void writeConfigFile(OutputStream outputStream, File searchDir) throws SQLException, IOException { 138 | List> classList = new ArrayList>(); 139 | findAnnotatedClasses(classList, searchDir, 0); 140 | writeConfigFile(outputStream, classList.toArray(new Class[classList.size()])); 141 | } 142 | 143 | /** 144 | * Write a configuration file to an output stream with the configuration for classes. 145 | */ 146 | public static void writeConfigFile(OutputStream outputStream, Class[] classes) throws SQLException, IOException { 147 | BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream), 4096); 148 | try { 149 | writeHeader(writer); 150 | for (Class clazz : classes) { 151 | writeConfigForTable(writer, clazz); 152 | } 153 | // NOTE: done is here because this is public 154 | System.out.println("Done."); 155 | } finally { 156 | writer.close(); 157 | } 158 | } 159 | 160 | /** 161 | * Look for the resource-directory in the current directory or the directories above. Then look for the 162 | * raw-directory underneath the resource-directory. 163 | */ 164 | protected static File findRawDir(File dir) { 165 | for (int i = 0; dir != null && i < 20; i++) { 166 | File rawDir = findResRawDir(dir); 167 | if (rawDir != null) { 168 | return rawDir; 169 | } 170 | dir = dir.getParentFile(); 171 | } 172 | return null; 173 | } 174 | 175 | private static void writeHeader(BufferedWriter writer) throws IOException { 176 | writer.append('#'); 177 | writer.newLine(); 178 | writer.append("# generated on ").append(new SimpleDateFormat("yyyy/MM/dd hh:mm:ss").format(new Date())); 179 | writer.newLine(); 180 | writer.append('#'); 181 | writer.newLine(); 182 | } 183 | 184 | private static void findAnnotatedClasses(List> classList, File dir, int level) throws SQLException, 185 | IOException { 186 | for (File file : dir.listFiles()) { 187 | if (file.isDirectory()) { 188 | // recurse if we aren't deep enough 189 | if (level < maxFindSourceLevel) { 190 | findAnnotatedClasses(classList, file, level + 1); 191 | } 192 | continue; 193 | } 194 | // skip non .java files 195 | if (!file.getName().endsWith(".java")) { 196 | continue; 197 | } 198 | String packageName = getPackageOfClass(file); 199 | if (packageName == null) { 200 | System.err.println("Could not find package name for: " + file); 201 | continue; 202 | } 203 | // get the filename and cut off the .java 204 | String name = file.getName(); 205 | name = name.substring(0, name.length() - ".java".length()); 206 | String className = packageName + "." + name; 207 | Class clazz; 208 | try { 209 | clazz = Class.forName(className); 210 | } catch (Throwable t) { 211 | // amazingly, this sometimes throws an Error 212 | System.err.println("Could not load class file for: " + file); 213 | System.err.println(" " + t); 214 | continue; 215 | } 216 | if (classHasAnnotations(clazz)) { 217 | classList.add(clazz); 218 | } 219 | // handle inner classes 220 | try { 221 | for (Class innerClazz : clazz.getDeclaredClasses()) { 222 | if (classHasAnnotations(innerClazz)) { 223 | classList.add(innerClazz); 224 | } 225 | } 226 | } catch (Throwable t) { 227 | // amazingly, this sometimes throws an Error 228 | System.err.println("Could not load inner classes for: " + clazz); 229 | System.err.println(" " + t); 230 | continue; 231 | } 232 | } 233 | } 234 | 235 | private static void writeConfigForTable(BufferedWriter writer, Class clazz) throws SQLException, IOException { 236 | String tableName = DatabaseTableConfig.extractTableName(clazz); 237 | List fieldConfigs = new ArrayList(); 238 | // walk up the classes finding the fields 239 | try { 240 | for (Class working = clazz; working != null; working = working.getSuperclass()) { 241 | for (Field field : working.getDeclaredFields()) { 242 | DatabaseFieldConfig fieldConfig = DatabaseFieldConfig.fromField(databaseType, tableName, field); 243 | if (fieldConfig != null) { 244 | fieldConfigs.add(fieldConfig); 245 | } 246 | } 247 | } 248 | } catch (Error e) { 249 | System.err.println("Skipping " + clazz + " because we got an error finding its definition: " 250 | + e.getMessage()); 251 | return; 252 | } 253 | if (fieldConfigs.isEmpty()) { 254 | System.out.println("Skipping " + clazz + " because no annotated fields found"); 255 | return; 256 | } 257 | @SuppressWarnings({ "rawtypes", "unchecked" }) 258 | DatabaseTableConfig tableConfig = new DatabaseTableConfig(clazz, tableName, fieldConfigs); 259 | DatabaseTableConfigLoader.write(writer, tableConfig); 260 | writer.append("#################################"); 261 | writer.newLine(); 262 | System.out.println("Wrote config for " + clazz); 263 | } 264 | 265 | private static boolean classHasAnnotations(Class clazz) { 266 | while (clazz != null) { 267 | if (clazz.getAnnotation(DatabaseTable.class) != null) { 268 | return true; 269 | } 270 | Field[] fields; 271 | try { 272 | fields = clazz.getDeclaredFields(); 273 | } catch (Throwable t) { 274 | // amazingly, this sometimes throws an Error 275 | System.err.println("Could not load get delcared fields from: " + clazz); 276 | System.err.println(" " + t); 277 | return false; 278 | } 279 | for (Field field : fields) { 280 | if (field.getAnnotation(DatabaseField.class) != null 281 | || field.getAnnotation(ForeignCollectionField.class) != null) { 282 | return true; 283 | } 284 | } 285 | try { 286 | clazz = clazz.getSuperclass(); 287 | } catch (Throwable t) { 288 | // amazingly, this sometimes throws an Error 289 | System.err.println("Could not get super class for: " + clazz); 290 | System.err.println(" " + t); 291 | return false; 292 | } 293 | } 294 | 295 | return false; 296 | } 297 | 298 | /** 299 | * Returns the package name of a file that has one of the annotations we are looking for. 300 | * 301 | * @return Package prefix string or null or no annotations. 302 | */ 303 | private static String getPackageOfClass(File file) throws IOException { 304 | BufferedReader reader = new BufferedReader(new FileReader(file)); 305 | try { 306 | while (true) { 307 | String line = reader.readLine(); 308 | if (line == null) { 309 | return null; 310 | } 311 | if (line.contains("package")) { 312 | String[] parts = line.split("[ \t;]"); 313 | if (parts.length > 1 && parts[0].equals("package")) { 314 | return parts[1]; 315 | } 316 | } 317 | } 318 | } finally { 319 | reader.close(); 320 | } 321 | } 322 | 323 | /** 324 | * Look for the resource directory with raw beneath it. 325 | */ 326 | private static File findResRawDir(File dir) { 327 | for (File file : dir.listFiles()) { 328 | if (file.getName().equals(RESOURCE_DIR_NAME) && file.isDirectory()) { 329 | File[] rawFiles = file.listFiles(new FileFilter() { 330 | public boolean accept(File file) { 331 | return file.getName().equals(RAW_DIR_NAME) && file.isDirectory(); 332 | } 333 | }); 334 | if (rawFiles.length == 1) { 335 | return rawFiles[0]; 336 | } 337 | } 338 | } 339 | return null; 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/compat/ApiCompatibility.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.compat; 2 | 3 | 4 | import net.sqlcipher.Cursor; 5 | import net.sqlcipher.database.SQLiteDatabase; 6 | 7 | /** 8 | * Compatibility interface to support various different versions of the Android API. 9 | * 10 | * @author graywatson 11 | */ 12 | public interface ApiCompatibility { 13 | 14 | /** 15 | * Perform a raw query on a database with an optional cancellation-hook. 16 | */ 17 | public Cursor rawQuery(SQLiteDatabase db, String sql, String[] selectionArgs, CancellationHook cancellationHook); 18 | 19 | /** 20 | * Return a cancellation hook object that will be passed to the 21 | * {@link #rawQuery(SQLiteDatabase, String, String[], CancellationHook)}. If not supported then this will return 22 | * null. 23 | */ 24 | public CancellationHook createCancellationHook(); 25 | 26 | /** 27 | * Cancellation hook class returned by {@link ApiCompatibility#createCancellationHook()}. 28 | */ 29 | public interface CancellationHook { 30 | /** 31 | * Cancel the associated query. 32 | */ 33 | public void cancel(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/compat/ApiCompatibilityUtils.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.compat; 2 | 3 | import android.os.Build; 4 | 5 | 6 | /** 7 | * Utility class which loads the various classes based on which API version is being supported. 8 | * 9 | * @author graywatson 10 | */ 11 | @SuppressWarnings("unused") 12 | public class ApiCompatibilityUtils { 13 | 14 | private static ApiCompatibility compatibility; 15 | 16 | /** 17 | * Copied from {@link Build.VERSION_CODES}. We don't use those codes because they won't be in certain versions of 18 | * Build. 19 | */ 20 | private static final int BASE = 1; 21 | private static final int BASE_1_1 = 2; 22 | private static final int CUPCAKE = 3; 23 | private static final int DONUT = 4; 24 | private static final int ECLAIR = 5; 25 | private static final int ECLAIR_0_1 = 6; 26 | private static final int ECLAIR_MR1 = 7; 27 | private static final int FROYO = 8; 28 | private static final int GINGERBREAD = 9; 29 | private static final int GINGERBREAD_MR1 = 10; 30 | private static final int HONEYCOMB = 11; 31 | private static final int HONEYCOMB_MR1 = 12; 32 | private static final int HONEYCOMB_MR2 = 13; 33 | private static final int ICE_CREAM_SANDWICH = 14; 34 | private static final int ICE_CREAM_SANDWICH_MR1 = 15; 35 | private static final int JELLY_BEAN = 16; 36 | private static final int JELLY_BEAN_MR1 = 17; 37 | private static final int JELLY_BEAN_MR2 = 18; 38 | 39 | static { 40 | if (Build.VERSION.SDK_INT >= JELLY_BEAN) { 41 | compatibility = new com.j256.ormlite.sqlcipher.android.compat.JellyBeanApiCompatibility(); 42 | } else { 43 | compatibility = new com.j256.ormlite.sqlcipher.android.compat.BasicApiCompatibility(); 44 | } 45 | } 46 | 47 | /** 48 | * Return the compatibility class that matches our build number. 49 | */ 50 | public static ApiCompatibility getCompatibility() { 51 | return compatibility; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/compat/BasicApiCompatibility.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.compat; 2 | 3 | 4 | import net.sqlcipher.Cursor; 5 | import net.sqlcipher.database.SQLiteDatabase; 6 | 7 | /** 8 | * Basic class which provides no-op methods for all Android version. 9 | * 10 | * @author graywatson 11 | */ 12 | public class BasicApiCompatibility implements ApiCompatibility { 13 | 14 | public Cursor rawQuery(SQLiteDatabase db, String sql, String[] selectionArgs, CancellationHook cancellationHook) { 15 | // NOTE: cancellationHook will always be null 16 | return db.rawQuery(sql, selectionArgs); 17 | } 18 | 19 | public CancellationHook createCancellationHook() { 20 | return null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/android/compat/JellyBeanApiCompatibility.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.android.compat; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.os.CancellationSignal; 6 | 7 | import net.sqlcipher.Cursor; 8 | import net.sqlcipher.database.SQLiteDatabase; 9 | 10 | /** 11 | * Basic class which provides no-op methods for all Android version. 12 | *

13 | *

14 | * NOTE: Will show as in error if compiled with previous Android versions. 15 | *

16 | * 17 | * @author graywatson 18 | */ 19 | public class JellyBeanApiCompatibility extends BasicApiCompatibility { 20 | 21 | @Override 22 | public Cursor rawQuery(SQLiteDatabase db, String sql, String[] selectionArgs, CancellationHook cancellationHook) { 23 | // NOTE: in patched version this is the same as BasicApiCompatibility 24 | // because SqlCipher supports Android version lower than API level 16 (Jelly Bean) 25 | // if (cancellationHook == null) { 26 | return db.rawQuery(sql, selectionArgs); 27 | // } else { 28 | // return db.rawQuery(sql, selectionArgs, ((JellyBeanCancellationHook) cancellationHook).cancellationSignal); 29 | // } 30 | } 31 | 32 | @Override 33 | public CancellationHook createCancellationHook() { 34 | return new JellyBeanCancellationHook(); 35 | } 36 | 37 | protected static class JellyBeanCancellationHook implements CancellationHook { 38 | 39 | private final CancellationSignal cancellationSignal; 40 | 41 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 42 | public JellyBeanCancellationHook() { 43 | this.cancellationSignal = new CancellationSignal(); 44 | } 45 | 46 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 47 | public void cancel() { 48 | cancellationSignal.cancel(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/com/j256/ormlite/sqlcipher/db/SqliteAndroidDatabaseType.java: -------------------------------------------------------------------------------- 1 | package com.j256.ormlite.sqlcipher.db; 2 | 3 | import com.j256.ormlite.db.BaseSqliteDatabaseType; 4 | import com.j256.ormlite.field.DataPersister; 5 | import com.j256.ormlite.field.FieldConverter; 6 | import com.j256.ormlite.field.FieldType; 7 | import com.j256.ormlite.field.types.DateStringType; 8 | import com.j256.ormlite.sqlcipher.android.DatabaseTableConfigUtil; 9 | import com.j256.ormlite.support.ConnectionSource; 10 | import com.j256.ormlite.table.DatabaseTableConfig; 11 | 12 | import java.sql.SQLException; 13 | 14 | /** 15 | * Sqlite database type information for the Android OS that makes native calls to the Android OS database APIs. 16 | * 17 | * @author graywatson 18 | */ 19 | public class SqliteAndroidDatabaseType extends BaseSqliteDatabaseType { 20 | 21 | @Override 22 | public void loadDriver() { 23 | // noop 24 | } 25 | 26 | public boolean isDatabaseUrlThisType(String url, String dbTypePart) { 27 | // not used by the android code 28 | return true; 29 | } 30 | 31 | @Override 32 | protected String getDriverClassName() { 33 | // no driver to load in android-land 34 | return null; 35 | } 36 | 37 | public String getDatabaseName() { 38 | return "Android SQLite"; 39 | } 40 | 41 | @Override 42 | protected void appendDateType(StringBuilder sb, FieldType fieldType, int fieldWidth) { 43 | // default is to store the date as a string 44 | appendStringType(sb, fieldType, fieldWidth); 45 | } 46 | 47 | @Override 48 | protected void appendBooleanType(StringBuilder sb, FieldType fieldType, int fieldWidth) { 49 | // we have to convert booleans to numbers 50 | appendShortType(sb, fieldType, fieldWidth); 51 | } 52 | 53 | @Override 54 | public FieldConverter getFieldConverter(DataPersister dataPersister) { 55 | // we are only overriding certain types 56 | switch (dataPersister.getSqlType()) { 57 | case DATE : 58 | return DateStringType.getSingleton(); 59 | default : 60 | return super.getFieldConverter(dataPersister); 61 | } 62 | } 63 | 64 | @Override 65 | public boolean isNestedSavePointsSupported() { 66 | return false; 67 | } 68 | 69 | @Override 70 | public boolean isBatchUseTransaction() { 71 | return true; 72 | } 73 | 74 | @Override 75 | public DatabaseTableConfig extractDatabaseTableConfig(ConnectionSource connectionSource, Class clazz) 76 | throws SQLException { 77 | return DatabaseTableConfigUtil.fromClass(connectionSource, clazz); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /sqlcipher/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | database 3 | 4 | --------------------------------------------------------------------------------