移除过时第三方库,同时把第三方库下载到本地,修改源代码适配最新的SDK后以模块的方式集成到项目内部

master
hwf452 2 years ago
parent 5edc4493c0
commit d185ba4e5f

@ -20,7 +20,6 @@ android {
}
buildFeatures {
viewBinding = true
buildConfig = true
}
signingConfigs {
release {
@ -37,7 +36,6 @@ android {
buildTypes {
debug {
minifyEnabled false
buildConfigField "boolean", "LOG_ERROR", "true"
}
release {
lintOptions {
@ -48,7 +46,6 @@ android {
zipAlignEnabled true // zip
debuggable false // debug
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField("boolean","LOG_ERROR","false");
//apk
android.applicationVariants.all { variant ->
variant.outputs.all {
@ -73,14 +70,12 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
//implementation 'androidx.appcompat:appcompat:1.2.0'
//implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.zhy:autolayout:1.4.5'
implementation 'com.yolanda.nohttp:nohttp:1.0.5'
//implementation 'com.zhy:autolayout:1.4.5'//
//implementation 'com.yolanda.nohttp:nohttp:1.0.5'//
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'org.litepal.android:core:1.5.1'
//implementation 'org.litepal.android:core:1.5.1'//
implementation 'com.github.huangyanbin:SmartTable:2.2.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'de.hdodenhof:circleimageview:3.1.0'
@ -89,10 +84,10 @@ dependencies {
implementation 'com.tencent.bugly:crashreport:3.2.1'
implementation 'com.github.xuexiangjys:XUI:1.1.4'
implementation 'org.greenrobot:eventbus:3.0.0'
implementation 'com.zhihu.android:matisse:0.5.3-beta3'
//implementation 'com.zhihu.android:matisse:0.5.3-beta3'//
//
implementation 'io.github.justson:agentweb-core:v5.1.1-androidx'
implementation 'com.lcodecorex:tkrefreshlayout:1.0.7'
//implementation 'com.lcodecorex:tkrefreshlayout:1.0.7'//
implementation 'androidx.multidex:multidex:2.0.1'
@ -100,7 +95,21 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
//
//
implementation project(':videocompressor')
//
implementation project(':nohttp')
//
implementation project(':autolayout')
//
implementation project(':litepal')
//
implementation project(':matisse')
//
implementation project(':tkrefreshlayout')
//SDK
implementation files('libs/SangforSDK.aar')
implementation 'de.hdodenhof:circleimageview:3.1.0'

@ -2,6 +2,9 @@ package com.rehome.dywoa;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.util.Log;
@ -52,6 +55,9 @@ public class App extends LitePalApplication {
XUI.init(this); //初始化UI框架
CrashReport.initCrashReport(getApplicationContext(), "f147ce3be5", true);
String applicationId = App.getApplicationId(context);
showLog(applicationId);
//YhpcFragment
}
@ -97,7 +103,7 @@ public class App extends LitePalApplication {
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null");
} else {
@ -134,4 +140,28 @@ public class App extends LitePalApplication {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
public static String getApplicationId(Context context) {
PackageManager packageManager = context.getPackageManager();
String packageName = context.getPackageName();
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
return applicationInfo.packageName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return null;
}
}
}

@ -4,8 +4,10 @@ package com.rehome.dywoa;
import static com.rehome.dywoa.utils.NfcUtil.stringsToBytes;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
@ -261,7 +263,7 @@ public class BaseNfcActivity extends AppCompatActivity {
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(BaseNfcActivity.this)) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
@ -270,6 +272,19 @@ public class BaseNfcActivity extends AppCompatActivity {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
/**
* toast
*

@ -85,14 +85,5 @@ public class CustomDialogActivity extends Activity {
super.onDestroy();
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
Log.i("app", logText);
}
}
}
}

@ -1777,12 +1777,13 @@ public class MainActivity extends BaseActivity2 {
InputStream fis = null;
OutputStream out = null;
try {
String applicationId = App.getApplicationId(context);
File fos = this.createFileIfNeed(path, filename);
Uri uriOut = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", fos);
Uri uriOut = FileProvider.getUriForFile(context, applicationId + ".fileprovider", fos);
out = getContentResolver().openOutputStream(uriOut);
fis = getContentResolver().openInputStream(uri);
IoUtil.copy(fis, out);
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", fos);
return FileProvider.getUriForFile(context, applicationId + ".fileprovider", fos);
} catch (Exception e) {
e.printStackTrace();
} finally {
@ -1874,8 +1875,9 @@ public class MainActivity extends BaseActivity2 {
* @throws IOException
*/
private void startPhotoZoom(Uri uri, String fileName, int flags) throws IOException {
String applicationId = App.getApplicationId(context);
File CropPhoto = this.createFileIfNeed(path, fileName);
uriTakePicOutputSt = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", CropPhoto);
uriTakePicOutputSt = FileProvider.getUriForFile(context, applicationId + ".fileprovider", CropPhoto);
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");

@ -2,6 +2,7 @@ package com.rehome.dywoa;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.JavascriptInterface;
@ -9,6 +10,7 @@ import android.widget.Toast;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.rehome.dywoa.ui.activity.BiShowActivity;
import com.rehome.dywoa.utils.SPUtils;
import java.net.URLDecoder;
@ -90,7 +92,7 @@ public class MyObject {
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
@ -99,6 +101,19 @@ public class MyObject {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
/**
*
* @return

@ -5,6 +5,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
@ -25,7 +26,6 @@ import android.widget.Toast;
import androidx.annotation.Nullable;
import com.rehome.dywoa.App;
import com.rehome.dywoa.AppManager;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.R;
import com.rehome.dywoa.bean.UserInfoBean;
import com.rehome.dywoa.utils.StatusBarUtil;
@ -318,7 +318,7 @@ public abstract class BaseActivity extends AutoLayoutActivity {
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
@ -326,4 +326,17 @@ public abstract class BaseActivity extends AutoLayoutActivity {
}
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}

@ -6,6 +6,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
@ -28,7 +29,6 @@ import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.R;
import com.rehome.dywoa.bean.UserInfoBean;
import com.rehome.dywoa.utils.ControllerActivity;
@ -405,7 +405,7 @@ public abstract class BaseActivity2 extends AutoLayoutActivity {
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
@ -413,4 +413,17 @@ public abstract class BaseActivity2 extends AutoLayoutActivity {
}
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}

@ -6,6 +6,7 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ApplicationInfo
import android.nfc.NdefMessage
import android.nfc.NfcAdapter
import android.nfc.tech.*
@ -17,7 +18,6 @@ import android.view.View
import android.widget.Toast
import androidx.viewbinding.ViewBinding
import com.rehome.dywoa.App
import com.rehome.dywoa.BuildConfig
import com.rehome.dywoa.R
import com.rehome.dywoa.bean.UserInfoBean
import com.rehome.dywoa.utils.ControllerActivity
@ -288,7 +288,7 @@ abstract class BaseActivityAutoToolbarViewBinding<T : ViewBinding> : AutoLayoutA
}
open fun showLog(logText: String?) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null")
} else {
@ -296,4 +296,16 @@ abstract class BaseActivityAutoToolbarViewBinding<T : ViewBinding> : AutoLayoutA
}
}
}
/**
* 判断当前应用是否是debug状态
*/
fun isApkInDebug(context: Context): Boolean {
try {
val info = context.applicationInfo
return (info.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
} catch (e: Exception) {
return false
}
}
}

@ -5,7 +5,7 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.content.pm.ApplicationInfo
import android.nfc.NdefMessage
import android.nfc.NfcAdapter
import android.nfc.tech.*
@ -18,7 +18,6 @@ import android.widget.EditText
import android.widget.Toast
import androidx.viewbinding.ViewBinding
import com.rehome.dywoa.App
import com.rehome.dywoa.BuildConfig
import com.rehome.dywoa.R
import com.rehome.dywoa.bean.UserInfoBean
import com.rehome.dywoa.utils.ControllerActivity
@ -339,7 +338,7 @@ abstract class BaseActivityOaToolbarViewBinding<T : ViewBinding> : AutoLayoutAct
mToolbar.setTvRightOnClickListener(listenerRightClick)
}
open fun showLog(logText: String?) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null")
} else {
@ -347,4 +346,16 @@ abstract class BaseActivityOaToolbarViewBinding<T : ViewBinding> : AutoLayoutAct
}
}
}
/**
* 判断当前应用是否是debug状态
*/
fun isApkInDebug(context: Context): Boolean {
try {
val info = context.applicationInfo
return (info.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
} catch (e: Exception) {
return false
}
}
}

@ -6,6 +6,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
@ -22,10 +23,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import androidx.viewbinding.ViewBinding;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.R;
import com.rehome.dywoa.utils.ControllerActivity;
import com.rehome.dywoa.weiget.OAToolbar;
@ -264,7 +262,7 @@ public abstract class BaseActivityViewBinding<T extends ViewBinding> extends Aut
mToolbar.setTvRightOnClickListener(listenerRightClick);
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
@ -272,4 +270,16 @@ public abstract class BaseActivityViewBinding<T extends ViewBinding> extends Aut
}
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}

@ -3,6 +3,7 @@ package com.rehome.dywoa.base;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
@ -12,7 +13,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.rehome.dywoa.BuildConfig;
/**
* Created by Rehome-rjb1 on 2017/5/8.
@ -83,7 +84,7 @@ public abstract class BaseFragment extends Fragment {
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null");
} else {
@ -91,4 +92,17 @@ public abstract class BaseFragment extends Fragment {
}
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}

@ -2,18 +2,18 @@ package com.rehome.dywoa.base;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.rehome.dywoa.BuildConfig;
/**
* Created by ruihong on 2018/4/20.
@ -124,7 +124,7 @@ public abstract class BaseLazyFragment extends Fragment {
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
@ -133,4 +133,17 @@ public abstract class BaseLazyFragment extends Fragment {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}

@ -6,7 +6,9 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
@ -21,38 +23,22 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.just.agentweb.AgentWeb;
import com.just.agentweb.DefaultWebClient;
import com.just.agentweb.WebChromeClient;
import com.just.agentweb.WebViewClient;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.R;
import com.rehome.dywoa.base.BaseAgentWebActivity;
import com.rehome.dywoa.bean.FwSingleLoginResult;
import com.rehome.dywoa.bean.SisTokenBeanData;
import com.rehome.dywoa.utils.GsonUtils;
import com.rehome.dywoa.utils.HttpListener;
import com.rehome.dywoa.utils.NoProgresshttpUtils;
import com.rehome.dywoa.utils.NohttpUtils;
import com.rehome.dywoa.utils.RSAUtils;
import com.rehome.dywoa.utils.UiUtlis;
import com.rehome.dywoa.weiget.WebLayout;
import com.yolanda.nohttp.NoHttp;
import com.yolanda.nohttp.RequestMethod;
import com.yolanda.nohttp.rest.Request;
import com.yolanda.nohttp.rest.Response;
import java.net.URLEncoder;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
public class BiShowActivity extends BaseAgentWebActivity {
private Toolbar mToolbar;
@ -222,7 +208,7 @@ public class BiShowActivity extends BaseAgentWebActivity {
private void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(BiShowActivity.this)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null");
} else {
@ -231,6 +217,19 @@ public class BiShowActivity extends BaseAgentWebActivity {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
public void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}

@ -57,10 +57,7 @@ import com.google.mlkit.vision.face.Face;
import com.google.mlkit.vision.face.FaceDetection;
import com.google.mlkit.vision.face.FaceDetector;
import com.google.mlkit.vision.face.FaceDetectorOptions;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.MainActivity;
import com.rehome.dywoa.R;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@ -72,9 +69,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import com.rehome.dywoa.base.BaseActivity;
import com.rehome.dywoa.base.BaseActivity2;
import com.rehome.dywoa.utils.BitmapUtil;
import com.rehome.dywoa.utils.BitmapUtils;
//import com.rehome.dywoa.utils.CameraManager;
@ -702,7 +697,7 @@ public class FaceRecognitionActivity extends BaseActivity {
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null");
} else {

@ -4,7 +4,9 @@ package com.rehome.dywoa.ui.activity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
@ -21,7 +23,6 @@ import com.just.agentweb.DefaultWebClient;
import com.just.agentweb.WebChromeClient;
import com.just.agentweb.WebViewClient;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.R;
import com.rehome.dywoa.bean.SisTokenBeanData;
@ -206,7 +207,7 @@ public class JiZhuActivity extends AppCompatActivity {
}
private void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(JiZhuActivity.this)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null");
} else {
@ -215,4 +216,17 @@ public class JiZhuActivity extends AppCompatActivity {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}

@ -28,7 +28,6 @@ import com.azhon.appupdate.listener.OnDownloadListener
import com.azhon.appupdate.manager.DownloadManager
import com.google.gson.Gson
import com.rehome.dywoa.App
import com.rehome.dywoa.BuildConfig
import com.rehome.dywoa.Contans
import com.rehome.dywoa.R
import com.rehome.dywoa.base.BaseActivityOaToolbarViewBinding
@ -187,10 +186,10 @@ class LoginActivity : BaseActivityOaToolbarViewBinding<ActivityLoginBinding>() {
binding.etPassword.setText(password)
}
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
//测试
// binding.etUsername.setText("371522")
// binding.etPassword.setText("A000000a")
binding.etUsername.setText("371522")
binding.etPassword.setText("A000000a")
//ceshi1
// binding.etUsername.setText("ceshi1")

@ -4,7 +4,9 @@ package com.rehome.dywoa.ui.activity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
@ -21,7 +23,6 @@ import com.just.agentweb.DefaultWebClient;
import com.just.agentweb.WebChromeClient;
import com.just.agentweb.WebViewClient;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.R;
import com.rehome.dywoa.bean.SisTokenBeanData;
@ -197,7 +198,7 @@ public class RunLogActivity extends AppCompatActivity {
}
private void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(RunLogActivity.this)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null");
} else {
@ -205,5 +206,16 @@ public class RunLogActivity extends AppCompatActivity {
}
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}

@ -3,6 +3,8 @@ package com.rehome.dywoa.ui.activity;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.Color;
import android.os.Bundle;
import android.text.TextUtils;
@ -12,9 +14,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.webkit.WebView;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.R;
import com.rehome.dywoa.base.BaseAgentWebActivity;
@ -175,7 +175,7 @@ public class SisBaseActivity extends BaseAgentWebActivity {
private void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(SisBaseActivity.this)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null");
} else {
@ -184,4 +184,17 @@ public class SisBaseActivity extends BaseAgentWebActivity {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}

@ -3,6 +3,7 @@ package com.rehome.dywoa.ui.activity
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.ApplicationInfo
import android.graphics.Bitmap
import android.graphics.Color
import android.os.Build
@ -22,7 +23,6 @@ import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import com.rehome.dywoa.App
import com.rehome.dywoa.BuildConfig
import com.rehome.dywoa.Contans
import com.rehome.dywoa.R
import com.rehome.dywoa.base.BaseActivityOaToolbarViewBinding
@ -213,7 +213,7 @@ class UseCarActivity : BaseAgentWebActivity() {
}
private fun showLog(logText: String) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null")
} else {
@ -222,6 +222,18 @@ class UseCarActivity : BaseAgentWebActivity() {
}
}
/**
* 判断当前应用是否是debug状态
*/
fun isApkInDebug(context: Context): Boolean {
try {
val info = context.applicationInfo
return (info.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
} catch (e: Exception) {
return false
}
}
fun showToast(msg: String?) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}

@ -3,6 +3,7 @@ package com.rehome.dywoa.ui.activity
import android.content.Context
import android.content.pm.ApplicationInfo
import android.graphics.Color
import android.os.Bundle
import android.text.TextUtils
@ -16,7 +17,6 @@ import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import com.rehome.dywoa.App
import com.rehome.dywoa.BuildConfig
import com.rehome.dywoa.Contans
import com.rehome.dywoa.R
import com.rehome.dywoa.base.BaseAgentWebActivity
@ -204,7 +204,7 @@ class UseSealActivity : BaseAgentWebActivity() {
}
private fun showLog(logText: String) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null")
} else {
@ -213,6 +213,18 @@ class UseSealActivity : BaseAgentWebActivity() {
}
}
/**
* 判断当前应用是否是debug状态
*/
fun isApkInDebug(context: Context): Boolean {
try {
val info = context.applicationInfo
return (info.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
} catch (e: Exception) {
return false
}
}
fun showToast(msg: String?) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}

@ -7,7 +7,9 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
@ -26,7 +28,6 @@ import com.just.agentweb.DefaultWebClient;
import com.just.agentweb.WebChromeClient;
import com.just.agentweb.WebViewClient;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.R;
import com.rehome.dywoa.base.BaseAgentWebActivity;
@ -257,7 +258,7 @@ public class WaitForToDoActivity extends BaseAgentWebActivity {
private void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(WaitForToDoActivity.this)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null");
} else {
@ -266,6 +267,19 @@ public class WaitForToDoActivity extends BaseAgentWebActivity {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
public void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}

@ -6,6 +6,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.Color;
import android.os.Bundle;
import android.text.TextUtils;
@ -17,14 +19,11 @@ import android.view.WindowManager;
import android.webkit.WebView;
import android.widget.TextView;
import android.widget.Toast;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.R;
import com.rehome.dywoa.base.BaseAgentWebActivity;
import com.rehome.dywoa.bean.FwSingleLoginResult;
import com.rehome.dywoa.bean.SisTokenBeanData;
import com.rehome.dywoa.utils.GsonUtils;
import com.rehome.dywoa.utils.HttpListener;
import com.rehome.dywoa.utils.NoProgresshttpUtils;
@ -249,7 +248,7 @@ public class WaitForToDoBaseActivity extends BaseAgentWebActivity {
private void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(WaitForToDoBaseActivity.this)) {
if (TextUtils.isEmpty(logText)) {
Log.i("app", "logText is null");
} else {
@ -258,6 +257,19 @@ public class WaitForToDoBaseActivity extends BaseAgentWebActivity {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
public void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}

@ -22,16 +22,13 @@ import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.DBModel.XcjsInfo;
import com.rehome.dywoa.DBModel.XfDjjhRwqy;
import com.rehome.dywoa.DBModel.XfXcjsInfo;
import com.rehome.dywoa.R;
import com.rehome.dywoa.base.BaseFragment;
import com.rehome.dywoa.utils.SPUtils;
import com.rehome.dywoa.utils.UiUtlis;
import java.io.File;
import java.util.Calendar;
@ -297,7 +294,8 @@ public class XcjsFragment extends BaseFragment {
uri = Uri.fromFile(fos);
uriTakePic = uri;
} else {
uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", fos);
String applicationId = App.getApplicationId(context);
uri = FileProvider.getUriForFile(context, applicationId + ".fileprovider", fos);
uriTakePic = uri;
}

@ -22,7 +22,6 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.gson.Gson;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.DBModel.Djjh;
import com.rehome.dywoa.DBModel.DjjhList;

@ -3,7 +3,9 @@ package com.rehome.dywoa.ui.activity.sbxdjgl;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@ -22,7 +24,6 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.DBModel.Ajhxzrwqy;
import com.rehome.dywoa.DBModel.YhpcInfo;
@ -33,7 +34,6 @@ import com.rehome.dywoa.entity.StatusInfo;
import com.rehome.dywoa.utils.GsonUtils;
import com.rehome.dywoa.utils.HttpListener;
import com.rehome.dywoa.utils.NohttpUtils;
import com.rehome.dywoa.utils.SPUtils;
import com.rehome.dywoa.utils.UiUtlis;
import com.yolanda.nohttp.NoHttp;
import com.yolanda.nohttp.RequestMethod;
@ -47,7 +47,7 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import com.rehome.dywoa.App;
/**
* Created by HuangWenfei on 2024/05/21.
@ -252,16 +252,20 @@ public class YhpcFragment extends BaseFragment {
uri = Uri.fromFile(fos);
uriTakePic=uri;
} else {
uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", fos);
String applicationId = App.getApplicationId(context);
uri = FileProvider.getUriForFile(context, applicationId + ".fileprovider", fos);
uriTakePic=uri;
}
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.Images.Media.ORIENTATION, 0);
i.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(i, 9);
}
/**
*
*/

@ -21,7 +21,8 @@ import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.App;
import com.rehome.dywoa.R;
import com.rehome.dywoa.base.BaseFragment;
import com.rehome.dywoa.utils.UiUtlis;
@ -163,13 +164,14 @@ public class ZxcjsFragment extends BaseFragment {
path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "/com.rehome.zhdcoa/images/";
File fos = new File(path + fileName);
// Uri u = Uri.fromFile(fos);
Uri u = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", fos);
String applicationId = App.getApplicationId(context);
Uri u = FileProvider.getUriForFile(context, applicationId + ".fileProvider", fos);
Uri uri;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
uri = Uri.fromFile(fos);
uriTakePic = uri;
} else {
uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", fos);
uri = FileProvider.getUriForFile(context, applicationId + ".fileprovider", fos);
uriTakePic = uri;
}

@ -34,10 +34,8 @@ import com.sangfor.sdk.base.SFBaseMessage;
import com.sangfor.sdk.base.SFTunnelStatus;
import com.sangfor.sdk.base.SFTunnelStatusListener;
import com.sangfor.sdk.utils.SFLogN;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.R;
import com.rehome.dywoa.utils.SignatureUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

@ -45,7 +45,6 @@ import com.luck.picture.lib.entity.LocalMedia;
import com.luck.picture.lib.interfaces.OnKeyValueResultCallbackListener;
import com.luck.picture.lib.interfaces.OnResultCallbackListener;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.DBModel.DjjhRwQy;
import com.rehome.dywoa.Listener.GlideEngine;
@ -614,7 +613,8 @@ public class MineFragment extends BaseLazyFragment {
private void cropFromTake() throws IOException {
this.deleteFileIfNeed(path, fileName);
File fos = this.createFileIfNeed(path, fileName);
Uri uri = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", fos);
String applicationId = App.getApplicationId(context);
Uri uri = FileProvider.getUriForFile(requireActivity(), applicationId + ".fileprovider", fos);
uriTakePic = uri;
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.Images.Media.ORIENTATION, 0);
@ -737,12 +737,13 @@ public class MineFragment extends BaseLazyFragment {
InputStream fis = null;
OutputStream out = null;
try {
String applicationId = App.getApplicationId(context);
File fos = this.createFileIfNeed(path, filename);
Uri uriOut=FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", fos);
Uri uriOut=FileProvider.getUriForFile(getActivity(), applicationId + ".fileprovider", fos);
out = getActivity().getContentResolver().openOutputStream(uriOut);
fis = getActivity().getContentResolver().openInputStream(uri);
IoUtil.copy(fis,out);
return FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", fos);
return FileProvider.getUriForFile(getActivity(), applicationId + ".fileprovider", fos);
} catch (Exception e) {
e.printStackTrace();
} finally {
@ -772,8 +773,9 @@ public class MineFragment extends BaseLazyFragment {
* @throws IOException
*/
private void startPhotoZoom(Uri uri, String fileName, int flags) throws IOException {
String applicationId = App.getApplicationId(context);
File CropPhoto = this.createFileIfNeed(path, fileName);
uriTakePicOutput = FileProvider.getUriForFile(requireActivity(), BuildConfig.APPLICATION_ID + ".fileprovider", CropPhoto);
uriTakePicOutput = FileProvider.getUriForFile(requireActivity(), applicationId + ".fileprovider", CropPhoto);
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");

@ -1,11 +1,13 @@
package com.rehome.dywoa.utils;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.util.Log;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.ui.activity.SisBaseActivity;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@ -59,8 +61,6 @@ public class BitmapCompressUtils {
newOpts.inSampleSize = be;// 设置缩放比例
// 重新读入图片注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
showLog("---------------");
showLog("w:"+String.valueOf(bitmap.getWidth()) + " h:" + bitmap.getHeight());
return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
}
/**
@ -84,8 +84,6 @@ public class BitmapCompressUtils {
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
showLog("---------------");
showLog("w:"+String.valueOf(w) + " h:" + h);
// 现在主流手机比较多是800*480分辨率所以高和宽我们设置为
// float hh = 800f;// 这里设置高度为800f
// float ww = 480f;// 这里设置宽度为480f
@ -105,19 +103,7 @@ public class BitmapCompressUtils {
// 重新读入图片注意此时已经把options.inJustDecodeBounds 设回false了
isBm = new ByteArrayInputStream(baos.toByteArray());
bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
showLog("---------------");
showLog("w:"+String.valueOf(bitmap.getWidth()) + " h:" + bitmap.getHeight());
return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
//return bitmap;
}
public static void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
Log.i("app", logText);
}
}
}
}

@ -2,6 +2,7 @@ package com.rehome.dywoa.utils;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
@ -31,7 +32,8 @@ import java.util.List;
import static com.rehome.dywoa.utils.BitmapUtils.ByteToBitmap;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.ui.activity.SisBaseActivity;
/**
* User:lizhangqu(513163535@qq.com)
@ -420,7 +422,7 @@ public class CameraManager implements Camera.PreviewCallback {
return rectBitmap;
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(mContext)) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
@ -428,6 +430,18 @@ public class CameraManager implements Camera.PreviewCallback {
}
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
public interface CunPictureOnPreview{
public void postBitmap(Bitmap rectBitmap,byte[] bytes);
}

@ -2,17 +2,15 @@ package com.rehome.dywoa.utils;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.ui.activity.SisBaseActivity;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
@ -131,7 +129,7 @@ public class HttpUtils {
}
public static void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
if (isApkInDebug(context)) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
@ -139,4 +137,17 @@ public class HttpUtils {
}
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}

@ -3,10 +3,10 @@ package com.rehome.dywoa.utils;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.text.TextUtils;
import android.util.Log;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.R;
import com.yolanda.nohttp.NoHttp;
import com.yolanda.nohttp.download.DownloadQueue;
@ -67,12 +67,12 @@ public class NoProgresshttpUtils {
if(DataPassUtils.checkCanLoginTokenLong()){
//showLog("之前");
String token = App.getInstance().getUserInfo().getToken();
showLog(token);
showLog(token,mActivity);
if(!TextUtils.isEmpty(token)){
String credential = "Bearer " + token;
request.addHeader("Authorization", credential);
request.addHeader("token", token);
showLog(request.url());
showLog(request.url(),mActivity);
}
}else{
@ -87,12 +87,12 @@ public class NoProgresshttpUtils {
if(DataPassUtils.checkCanQj()){
//showLog("之前");
String token = App.getInstance().getUserInfo().getToken();
showLog(token);
showLog(token,mActivity);
if(!TextUtils.isEmpty(token)){
String credential = "Bearer " + token;
request.addHeader("Authorization", credential);
request.addHeader("token", token);
showLog(request.url());
showLog(request.url(),mActivity);
}
}else{
@ -108,12 +108,12 @@ public class NoProgresshttpUtils {
if(DataPassUtils.checkCanDj()){
//showLog("之前");
String token = App.getInstance().getUserInfo().getToken();
showLog(token);
showLog(token,mActivity);
if(!TextUtils.isEmpty(token)){
String credential = "Bearer " + token;
request.addHeader("Authorization", credential);
request.addHeader("token", token);
showLog(request.url());
showLog(request.url(),mActivity);
}
}else{
@ -123,8 +123,8 @@ public class NoProgresshttpUtils {
mQueue.add(what, request, new HttpResponseListenerNoProgress<T>(request, callback));
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
public void showLog(String logText,Context context) {
if (isApkInDebug(context)) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
@ -133,6 +133,19 @@ public class NoProgresshttpUtils {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
/**
* sign
*

@ -3,13 +3,13 @@ package com.rehome.dywoa.utils;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.text.TextUtils;
import android.util.Log;
import com.rehome.dywoa.App;
import com.rehome.dywoa.BuildConfig;
import com.rehome.dywoa.Contans;
import com.rehome.dywoa.R;
import com.rehome.dywoa.ui.activity.SisBaseActivity;
import com.yolanda.nohttp.NoHttp;
import com.yolanda.nohttp.download.DownloadQueue;
import com.yolanda.nohttp.rest.Request;
@ -82,12 +82,12 @@ public class NohttpUtils {
if(DataPassUtils.checkCanLoginTokenLong()){
//showLog("之前");
String token = App.getInstance().getUserInfo().getToken();
showLog(token);
showLog(token,mActivity);
if(!TextUtils.isEmpty(token)){
String credential = "Bearer " + token;
request.addHeader("Authorization", credential);
request.addHeader("token", token);
showLog(request.url());
showLog(request.url(),mActivity);
}
}else{
@ -103,12 +103,12 @@ public class NohttpUtils {
if(DataPassUtils.checkCanLoginTokenLong()){
//showLog("之前");
String token = App.getInstance().getUserInfo().getToken();
showLog(token);
showLog(token,mActivity);
if(!TextUtils.isEmpty(token)){
String credential = "Bearer " + token;
request.addHeader("Authorization", credential);
request.addHeader("token", token);
showLog(request.url());
showLog(request.url(),mActivity);
}
}else{
@ -123,12 +123,12 @@ public class NohttpUtils {
if(DataPassUtils.checkCanLoginTokenLong()){
//showLog("之前");
String token = App.getInstance().getUserInfo().getToken();
showLog(token);
showLog(token,context);
if(!TextUtils.isEmpty(token)){
String credential = "Bearer " + token;
request.addHeader("Authorization", credential);
request.addHeader("token", token);
showLog(request.url());
showLog(request.url(),context);
}
}else{
@ -138,8 +138,8 @@ public class NohttpUtils {
mQueue.add(what, request, new HttpResponseListenerNoProgress<T>(request, callback));
}
public void showLog(String logText) {
if (BuildConfig.LOG_ERROR) {
public void showLog(String logText,Context context) {
if (isApkInDebug(context)) {
if(TextUtils.isEmpty(logText)){
Log.i("app", "logText is null");
}else{
@ -148,6 +148,19 @@ public class NohttpUtils {
}
}
/**
* debug
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
/**
* sign
*

@ -0,0 +1,22 @@
apply plugin: 'com.android.library'
android {
compileSdk 34
defaultConfig {
minSdk 24
targetSdk 34
versionName "1.4.5"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
namespace 'com.zhy.autolayout'
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.7.0'
}

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/zhy/android/sdk/android-sdk-macosx/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

@ -0,0 +1,15 @@
package zhy.com.autolayout;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application>
{
public ApplicationTest()
{
super(Application.class);
}
}

@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

@ -0,0 +1,125 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zhy.autolayout;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.zhy.autolayout.utils.AutoLayoutHelper;
public class AutoFrameLayout extends FrameLayout
{
private final AutoLayoutHelper mHelper = new AutoLayoutHelper(this);
public AutoFrameLayout(Context context)
{
super(context);
}
public AutoFrameLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public AutoFrameLayout(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public AutoFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new LayoutParams(getContext(), attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (!isInEditMode())
{
mHelper.adjustChildren();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
super.onLayout(changed, left, top, right, bottom);
}
public static class LayoutParams extends FrameLayout.LayoutParams
implements AutoLayoutHelper.AutoLayoutParams
{
private AutoLayoutInfo mAutoLayoutInfo;
public LayoutParams(Context c, AttributeSet attrs)
{
super(c, attrs);
mAutoLayoutInfo = AutoLayoutHelper.getAutoLayoutInfo(c, attrs);
}
public LayoutParams(int width, int height)
{
super(width, height);
}
public LayoutParams(int width, int height, int gravity)
{
super(width, height, gravity);
}
public LayoutParams(ViewGroup.LayoutParams source)
{
super(source);
}
public LayoutParams(MarginLayoutParams source)
{
super(source);
}
public LayoutParams(FrameLayout.LayoutParams source)
{
super((MarginLayoutParams) source);
gravity = source.gravity;
}
public LayoutParams(LayoutParams source)
{
this((FrameLayout.LayoutParams) source);
mAutoLayoutInfo = source.mAutoLayoutInfo;
}
@Override
public AutoLayoutInfo getAutoLayoutInfo()
{
return mAutoLayoutInfo;
}
}
}

@ -0,0 +1,45 @@
package com.zhy.autolayout;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
;import androidx.appcompat.app.AppCompatActivity;
/**
* Created by zhy on 15/11/19.
*/
public class AutoLayoutActivity extends AppCompatActivity
{
private static final String LAYOUT_LINEARLAYOUT = "LinearLayout";
private static final String LAYOUT_FRAMELAYOUT = "FrameLayout";
private static final String LAYOUT_RELATIVELAYOUT = "RelativeLayout";
@Override
public View onCreateView(String name, Context context, AttributeSet attrs)
{
View view = null;
if (name.equals(LAYOUT_FRAMELAYOUT))
{
view = new AutoFrameLayout(context, attrs);
}
if (name.equals(LAYOUT_LINEARLAYOUT))
{
view = new AutoLinearLayout(context, attrs);
}
if (name.equals(LAYOUT_RELATIVELAYOUT))
{
view = new AutoRelativeLayout(context, attrs);
}
if (view != null) return view;
return super.onCreateView(name, context, attrs);
}
}

@ -0,0 +1,155 @@
package com.zhy.autolayout;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.zhy.autolayout.attr.Attrs;
import com.zhy.autolayout.attr.AutoAttr;
import com.zhy.autolayout.attr.HeightAttr;
import com.zhy.autolayout.attr.MarginBottomAttr;
import com.zhy.autolayout.attr.MarginLeftAttr;
import com.zhy.autolayout.attr.MarginRightAttr;
import com.zhy.autolayout.attr.MarginTopAttr;
import com.zhy.autolayout.attr.MaxHeightAttr;
import com.zhy.autolayout.attr.MaxWidthAttr;
import com.zhy.autolayout.attr.MinHeightAttr;
import com.zhy.autolayout.attr.MinWidthAttr;
import com.zhy.autolayout.attr.PaddingBottomAttr;
import com.zhy.autolayout.attr.PaddingLeftAttr;
import com.zhy.autolayout.attr.PaddingRightAttr;
import com.zhy.autolayout.attr.PaddingTopAttr;
import com.zhy.autolayout.attr.TextSizeAttr;
import com.zhy.autolayout.attr.WidthAttr;
import java.util.ArrayList;
import java.util.List;
public class AutoLayoutInfo
{
private List<AutoAttr> autoAttrs = new ArrayList<>();
public void addAttr(AutoAttr autoAttr)
{
autoAttrs.add(autoAttr);
}
public void fillAttrs(View view)
{
for (AutoAttr autoAttr : autoAttrs)
{
autoAttr.apply(view);
}
}
public static AutoLayoutInfo getAttrFromView(View view, int attrs, int base)
{
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params == null) return null;
AutoLayoutInfo autoLayoutInfo = new AutoLayoutInfo();
// width & height
if ((attrs & Attrs.WIDTH) != 0 && params.width > 0)
{
autoLayoutInfo.addAttr(WidthAttr.generate(params.width, base));
}
if ((attrs & Attrs.HEIGHT) != 0 && params.height > 0)
{
autoLayoutInfo.addAttr(HeightAttr.generate(params.height, base));
}
//margin
if (params instanceof ViewGroup.MarginLayoutParams)
{
if ((attrs & Attrs.MARGIN) != 0)
{
autoLayoutInfo.addAttr(MarginLeftAttr.generate(((ViewGroup.MarginLayoutParams) params).leftMargin, base));
autoLayoutInfo.addAttr(MarginTopAttr.generate(((ViewGroup.MarginLayoutParams) params).topMargin, base));
autoLayoutInfo.addAttr(MarginRightAttr.generate(((ViewGroup.MarginLayoutParams) params).rightMargin, base));
autoLayoutInfo.addAttr(MarginBottomAttr.generate(((ViewGroup.MarginLayoutParams) params).bottomMargin, base));
}
if ((attrs & Attrs.MARGIN_LEFT) != 0)
{
autoLayoutInfo.addAttr(MarginLeftAttr.generate(((ViewGroup.MarginLayoutParams) params).leftMargin, base));
}
if ((attrs & Attrs.MARGIN_TOP) != 0)
{
autoLayoutInfo.addAttr(MarginTopAttr.generate(((ViewGroup.MarginLayoutParams) params).topMargin, base));
}
if ((attrs & Attrs.MARGIN_RIGHT) != 0)
{
autoLayoutInfo.addAttr(MarginRightAttr.generate(((ViewGroup.MarginLayoutParams) params).rightMargin, base));
}
if ((attrs & Attrs.MARGIN_BOTTOM) != 0)
{
autoLayoutInfo.addAttr(MarginBottomAttr.generate(((ViewGroup.MarginLayoutParams) params).bottomMargin, base));
}
}
//padding
if ((attrs & Attrs.PADDING) != 0)
{
autoLayoutInfo.addAttr(PaddingLeftAttr.generate(view.getPaddingLeft(), base));
autoLayoutInfo.addAttr(PaddingTopAttr.generate(view.getPaddingTop(), base));
autoLayoutInfo.addAttr(PaddingRightAttr.generate(view.getPaddingRight(), base));
autoLayoutInfo.addAttr(PaddingBottomAttr.generate(view.getPaddingBottom(), base));
}
if ((attrs & Attrs.PADDING_LEFT) != 0)
{
autoLayoutInfo.addAttr(MarginLeftAttr.generate(view.getPaddingLeft(), base));
}
if ((attrs & Attrs.PADDING_TOP) != 0)
{
autoLayoutInfo.addAttr(MarginTopAttr.generate(view.getPaddingTop(), base));
}
if ((attrs & Attrs.PADDING_RIGHT) != 0)
{
autoLayoutInfo.addAttr(MarginRightAttr.generate(view.getPaddingRight(), base));
}
if ((attrs & Attrs.PADDING_BOTTOM) != 0)
{
autoLayoutInfo.addAttr(MarginBottomAttr.generate(view.getPaddingBottom(), base));
}
//minWidth ,maxWidth , minHeight , maxHeight
if ((attrs & Attrs.MIN_WIDTH) != 0)
{
autoLayoutInfo.addAttr(MinWidthAttr.generate(MinWidthAttr.getMinWidth(view), base));
}
if ((attrs & Attrs.MAX_WIDTH) != 0)
{
autoLayoutInfo.addAttr(MaxWidthAttr.generate(MaxWidthAttr.getMaxWidth(view), base));
}
if ((attrs & Attrs.MIN_HEIGHT) != 0)
{
autoLayoutInfo.addAttr(MinHeightAttr.generate(MinHeightAttr.getMinHeight(view), base));
}
if ((attrs & Attrs.MAX_HEIGHT) != 0)
{
autoLayoutInfo.addAttr(MaxHeightAttr.generate(MaxHeightAttr.getMaxHeight(view), base));
}
//textsize
if (view instanceof TextView)
{
if ((attrs & Attrs.TEXTSIZE) != 0)
{
autoLayoutInfo.addAttr(TextSizeAttr.generate((int) ((TextView) view).getTextSize(), base));
}
}
return autoLayoutInfo;
}
@Override
public String toString()
{
return "AutoLayoutInfo{" +
"autoAttrs=" + autoAttrs +
'}';
}
}

@ -0,0 +1,97 @@
package com.zhy.autolayout;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.zhy.autolayout.utils.AutoLayoutHelper;
/**
* Created by zhy on 15/6/30.
*/
public class AutoLinearLayout extends LinearLayout
{
private AutoLayoutHelper mHelper = new AutoLayoutHelper(this);
public AutoLinearLayout(Context context) {
super(context);
}
public AutoLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public AutoLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public AutoLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (!isInEditMode())
mHelper.adjustChildren();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
super.onLayout(changed, l, t, r, b);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new AutoLinearLayout.LayoutParams(getContext(), attrs);
}
public static class LayoutParams extends LinearLayout.LayoutParams
implements AutoLayoutHelper.AutoLayoutParams
{
private AutoLayoutInfo mAutoLayoutInfo;
public LayoutParams(Context c, AttributeSet attrs)
{
super(c, attrs);
mAutoLayoutInfo = AutoLayoutHelper.getAutoLayoutInfo(c, attrs);
}
@Override
public AutoLayoutInfo getAutoLayoutInfo()
{
return mAutoLayoutInfo;
}
public LayoutParams(int width, int height)
{
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source)
{
super(source);
}
public LayoutParams(MarginLayoutParams source)
{
super(source);
}
}
}

@ -0,0 +1,107 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zhy.autolayout;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import com.zhy.autolayout.utils.AutoLayoutHelper;
public class AutoRelativeLayout extends RelativeLayout
{
private final AutoLayoutHelper mHelper = new AutoLayoutHelper(this);
public AutoRelativeLayout(Context context)
{
super(context);
}
public AutoRelativeLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public AutoRelativeLayout(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public AutoRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new LayoutParams(getContext(), attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (!isInEditMode())
mHelper.adjustChildren();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
super.onLayout(changed, left, top, right, bottom);
}
public static class LayoutParams extends RelativeLayout.LayoutParams
implements AutoLayoutHelper.AutoLayoutParams
{
private AutoLayoutInfo mAutoLayoutInfo;
public LayoutParams(Context c, AttributeSet attrs)
{
super(c, attrs);
mAutoLayoutInfo = AutoLayoutHelper.getAutoLayoutInfo(c, attrs);
}
public LayoutParams(int width, int height)
{
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source)
{
super(source);
}
public LayoutParams(MarginLayoutParams source)
{
super(source);
}
@Override
public AutoLayoutInfo getAutoLayoutInfo()
{
return mAutoLayoutInfo;
}
}
}

@ -0,0 +1,28 @@
package com.zhy.autolayout.attr;
/**
* Created by zhy on 15/12/5.
* <p/>
* attrs.xml
*/
public interface Attrs
{
public static final int WIDTH = 1;
public static final int HEIGHT = WIDTH << 1;
public static final int TEXTSIZE = HEIGHT << 1;
public static final int PADDING = TEXTSIZE << 1;
public static final int MARGIN = PADDING << 1;
public static final int MARGIN_LEFT = MARGIN << 1;
public static final int MARGIN_TOP = MARGIN_LEFT << 1;
public static final int MARGIN_RIGHT = MARGIN_TOP << 1;
public static final int MARGIN_BOTTOM = MARGIN_RIGHT << 1;
public static final int PADDING_LEFT = MARGIN_BOTTOM << 1;
public static final int PADDING_TOP = PADDING_LEFT << 1;
public static final int PADDING_RIGHT = PADDING_TOP << 1;
public static final int PADDING_BOTTOM = PADDING_RIGHT << 1;
public static final int MIN_WIDTH = PADDING_BOTTOM << 1;
public static final int MAX_WIDTH = MIN_WIDTH << 1;
public static final int MIN_HEIGHT = MAX_WIDTH << 1;
public static final int MAX_HEIGHT = MIN_HEIGHT << 1;
}

@ -0,0 +1,126 @@
package com.zhy.autolayout.attr;
import android.view.View;
import com.zhy.autolayout.utils.AutoUtils;
import com.zhy.autolayout.utils.L;
/**
* Created by zhy on 15/12/4.
*/
public abstract class AutoAttr
{
public static final int BASE_WIDTH = 1;
public static final int BASE_HEIGHT = 2;
public static final int BASE_DEFAULT = 3;
protected int pxVal;
protected int baseWidth;
protected int baseHeight;
/*
protected boolean isBaseWidth;
protected boolean isBaseDefault;
public AutoAttr(int pxVal)
{
this.pxVal = pxVal;
isBaseDefault = true;
}
public AutoAttr(int pxVal, boolean isBaseWidth)
{
this.pxVal = pxVal;
this.isBaseWidth = isBaseWidth;
}
*/
public AutoAttr(int pxVal, int baseWidth, int baseHeight)
{
this.pxVal = pxVal;
this.baseWidth = baseWidth;
this.baseHeight = baseHeight;
}
public void apply(View view)
{
boolean log = view.getTag() != null && view.getTag().toString().equals("auto");
if (log)
{
L.e(" pxVal = " + pxVal + " ," + this.getClass().getSimpleName());
}
int val;
if (useDefault())
{
val = defaultBaseWidth() ? getPercentWidthSize() : getPercentHeightSize();
if (log)
{
L.e(" useDefault val= " + val);
}
} else if (baseWidth())
{
val = getPercentWidthSize();
if (log)
{
L.e(" baseWidth val= " + val);
}
} else
{
val = getPercentHeightSize();
if (log)
{
L.e(" baseHeight val= " + val);
}
}
if (val > 0)
val = Math.max(val, 1);//for very thin divider
execute(view, val);
}
protected int getPercentWidthSize()
{
return AutoUtils.getPercentWidthSizeBigger(pxVal);
}
protected int getPercentHeightSize()
{
return AutoUtils.getPercentHeightSizeBigger(pxVal);
}
protected boolean baseWidth()
{
return contains(baseWidth, attrVal());
}
protected boolean useDefault()
{
return !contains(baseHeight, attrVal()) && !contains(baseWidth, attrVal());
}
protected boolean contains(int baseVal, int flag)
{
return (baseVal & flag) != 0;
}
protected abstract int attrVal();
protected abstract boolean defaultBaseWidth();
protected abstract void execute(View view, int val);
@Override
public String toString()
{
return "AutoAttr{" +
"pxVal=" + pxVal +
", baseWidth=" + baseWidth() +
", defaultBaseWidth=" + defaultBaseWidth() +
'}';
}
}

@ -0,0 +1,54 @@
package com.zhy.autolayout.attr;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by zhy on 15/12/5.
*/
public class HeightAttr extends AutoAttr
{
public HeightAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.HEIGHT;
}
@Override
protected boolean defaultBaseWidth()
{
return false;
}
@Override
protected void execute(View view, int val)
{
ViewGroup.LayoutParams lp = view.getLayoutParams();
lp.height = val;
}
public static HeightAttr generate(int val, int baseFlag)
{
HeightAttr heightAttr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
heightAttr = new HeightAttr(val, Attrs.HEIGHT, 0);
break;
case AutoAttr.BASE_HEIGHT:
heightAttr = new HeightAttr(val, 0, Attrs.HEIGHT);
break;
case AutoAttr.BASE_DEFAULT:
heightAttr = new HeightAttr(val, 0, 0);
break;
}
return heightAttr;
}
}

@ -0,0 +1,52 @@
package com.zhy.autolayout.attr;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by zhy on 15/12/5.
*/
public class MarginAttr extends AutoAttr
{
public MarginAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.MARGIN;
}
@Override
protected boolean defaultBaseWidth()
{
return false;
}
@Override
public void apply(View view)
{
if (!(view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams))
{
return;
}
if (useDefault())
{
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
lp.leftMargin = lp.rightMargin = getPercentWidthSize();
lp.topMargin = lp.bottomMargin = getPercentHeightSize();
return;
}
super.apply(view);
}
@Override
protected void execute(View view, int val)
{
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
lp.leftMargin = lp.rightMargin = lp.topMargin = lp.bottomMargin = val;
}
}

@ -0,0 +1,56 @@
package com.zhy.autolayout.attr;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by zhy on 15/12/5.
*/
public class MarginBottomAttr extends AutoAttr
{
public MarginBottomAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.MARGIN_BOTTOM;
}
@Override
protected boolean defaultBaseWidth()
{
return false;
}
@Override
protected void execute(View view, int val)
{
if(!(view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams))
{
return ;
}
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
lp.bottomMargin = val;
}
public static MarginBottomAttr generate(int val, int baseFlag)
{
MarginBottomAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new MarginBottomAttr(val, Attrs.MARGIN_BOTTOM, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new MarginBottomAttr(val, 0, Attrs.MARGIN_BOTTOM);
break;
case AutoAttr.BASE_DEFAULT:
attr = new MarginBottomAttr(val, 0, 0);
break;
}
return attr;
}
}

@ -0,0 +1,56 @@
package com.zhy.autolayout.attr;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by zhy on 15/12/5.
*/
public class MarginLeftAttr extends AutoAttr
{
public MarginLeftAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.MARGIN_LEFT;
}
@Override
protected boolean defaultBaseWidth()
{
return true;
}
@Override
protected void execute(View view, int val)
{
if (!(view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams))
{
return;
}
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
lp.leftMargin = val;
}
public static MarginLeftAttr generate(int val, int baseFlag)
{
MarginLeftAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new MarginLeftAttr(val, Attrs.MARGIN_LEFT, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new MarginLeftAttr(val, 0, Attrs.MARGIN_LEFT);
break;
case AutoAttr.BASE_DEFAULT:
attr = new MarginLeftAttr(val, 0, 0);
break;
}
return attr;
}
}

@ -0,0 +1,57 @@
package com.zhy.autolayout.attr;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by zhy on 15/12/5.
*/
public class MarginRightAttr extends AutoAttr
{
public MarginRightAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.MARGIN_RIGHT;
}
@Override
protected boolean defaultBaseWidth()
{
return true;
}
@Override
protected void execute(View view, int val)
{
if (!(view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams))
{
return;
}
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
lp.rightMargin = val;
}
public static MarginRightAttr generate(int val, int baseFlag)
{
MarginRightAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new MarginRightAttr(val, Attrs.MARGIN_RIGHT, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new MarginRightAttr(val, 0, Attrs.MARGIN_RIGHT);
break;
case AutoAttr.BASE_DEFAULT:
attr = new MarginRightAttr(val, 0, 0);
break;
}
return attr;
}
}

@ -0,0 +1,58 @@
package com.zhy.autolayout.attr;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by zhy on 15/12/5.
*/
public class MarginTopAttr extends AutoAttr
{
public MarginTopAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.MARGIN_TOP;
}
@Override
protected boolean defaultBaseWidth()
{
return false;
}
@Override
protected void execute(View view, int val)
{
if (!(view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams))
{
return;
}
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
lp.topMargin = val;
}
public static MarginTopAttr generate(int val, int baseFlag)
{
MarginTopAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new MarginTopAttr(val, Attrs.MARGIN_TOP, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new MarginTopAttr(val, 0, Attrs.MARGIN_TOP);
break;
case AutoAttr.BASE_DEFAULT:
attr = new MarginTopAttr(val, 0, 0);
break;
}
return attr;
}
}

@ -0,0 +1,70 @@
package com.zhy.autolayout.attr;
import android.view.View;
import java.lang.reflect.Method;
/**
* Created by zhy on 15/12/24.
*/
public class MaxHeightAttr extends AutoAttr
{
public MaxHeightAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.MAX_HEIGHT;
}
@Override
protected boolean defaultBaseWidth()
{
return false;
}
@Override
protected void execute(View view, int val)
{
try
{
Method setMaxWidthMethod = view.getClass().getMethod("setMaxHeight", int.class);
setMaxWidthMethod.invoke(view, val);
} catch (Exception ignore)
{
}
}
public static MaxHeightAttr generate(int val, int baseFlag)
{
MaxHeightAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new MaxHeightAttr(val, Attrs.MAX_HEIGHT, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new MaxHeightAttr(val, 0, Attrs.MAX_HEIGHT);
break;
case AutoAttr.BASE_DEFAULT:
attr = new MaxHeightAttr(val, 0, 0);
break;
}
return attr;
}
public static int getMaxHeight(View view)
{
try
{
Method setMaxWidthMethod = view.getClass().getMethod("getMaxHeight");
return (int) setMaxWidthMethod.invoke(view);
} catch (Exception ignore)
{
}
return 0;
}
}

@ -0,0 +1,70 @@
package com.zhy.autolayout.attr;
import android.view.View;
import java.lang.reflect.Method;
/**
* Created by zhy on 15/12/24.
*/
public class MaxWidthAttr extends AutoAttr
{
public MaxWidthAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.MAX_WIDTH;
}
@Override
protected boolean defaultBaseWidth()
{
return true;
}
@Override
protected void execute(View view, int val)
{
try
{
Method setMaxWidthMethod = view.getClass().getMethod("setMaxWidth", int.class);
setMaxWidthMethod.invoke(view, val);
} catch (Exception ignore)
{
}
}
public static MaxWidthAttr generate(int val, int baseFlag)
{
MaxWidthAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new MaxWidthAttr(val, Attrs.MAX_WIDTH, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new MaxWidthAttr(val, 0, Attrs.MAX_WIDTH);
break;
case AutoAttr.BASE_DEFAULT:
attr = new MaxWidthAttr(val, 0, 0);
break;
}
return attr;
}
public static int getMaxWidth(View view)
{
try
{
Method setMaxWidthMethod = view.getClass().getMethod("getMaxWidth");
return (int) setMaxWidthMethod.invoke(view);
} catch (Exception ignore)
{
}
return 0;
}
}

@ -0,0 +1,81 @@
package com.zhy.autolayout.attr;
import android.os.Build;
import android.view.View;
import java.lang.reflect.Field;
/**
* Created by zhy on 15/12/24.
*/
public class MinHeightAttr extends AutoAttr
{
public MinHeightAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.MIN_HEIGHT;
}
@Override
protected boolean defaultBaseWidth()
{
return false;
}
@Override
protected void execute(View view, int val)
{
try
{
view.setMinimumHeight(val);
// Method setMaxWidthMethod = view.getClass().getMethod("setMinHeight", int.class);
// setMaxWidthMethod.invoke(view, val);
} catch (Exception ignore)
{
}
}
public static MinHeightAttr generate(int val, int baseFlag)
{
MinHeightAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new MinHeightAttr(val, Attrs.MIN_HEIGHT, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new MinHeightAttr(val, 0, Attrs.MIN_HEIGHT);
break;
case AutoAttr.BASE_DEFAULT:
attr = new MinHeightAttr(val, 0, 0);
break;
}
return attr;
}
public static int getMinHeight(View view)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
return view.getMinimumHeight();
} else
{
try
{
Field minHeight = view.getClass().getField("mMinHeight");
minHeight.setAccessible(true);
return (int) minHeight.get(view);
} catch (Exception e)
{
}
}
return 0;
}
}

@ -0,0 +1,77 @@
package com.zhy.autolayout.attr;
import android.os.Build;
import android.view.View;
import java.lang.reflect.Field;
/**
* Created by zhy on 15/12/24.
*/
public class MinWidthAttr extends AutoAttr
{
public MinWidthAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.MIN_WIDTH;
}
@Override
protected boolean defaultBaseWidth()
{
return true;
}
@Override
protected void execute(View view, int val)
{
try
{
// Method setMaxWidthMethod = view.getClass().getMethod("setMinWidth", int.class);
// setMaxWidthMethod.invoke(view, val);
} catch (Exception ignore)
{
}
view.setMinimumWidth(val);
}
public static int getMinWidth(View view)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
return view.getMinimumWidth();
try
{
Field minWidth = view.getClass().getField("mMinWidth");
minWidth.setAccessible(true);
return (int) minWidth.get(view);
} catch (Exception ignore)
{
}
return 0;
}
public static MinWidthAttr generate(int val, int baseFlag)
{
MinWidthAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new MinWidthAttr(val, Attrs.MIN_WIDTH, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new MinWidthAttr(val, 0, Attrs.MIN_WIDTH);
break;
case AutoAttr.BASE_DEFAULT:
attr = new MinWidthAttr(val, 0, 0);
break;
}
return attr;
}
}

@ -0,0 +1,49 @@
package com.zhy.autolayout.attr;
import android.view.View;
/**
* Created by zhy on 15/12/5.
*/
public class PaddingAttr extends AutoAttr
{
public PaddingAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.PADDING;
}
@Override
public void apply(View view)
{
int l, t, r, b;
if (useDefault())
{
l = r = getPercentWidthSize();
t = b = getPercentHeightSize();
view.setPadding(l, t, r, b);
return;
}
super.apply(view);
}
@Override
protected boolean defaultBaseWidth()
{
return false;
}
@Override
protected void execute(View view, int val)
{
view.setPadding(val, val, val, val);
}
}

@ -0,0 +1,56 @@
package com.zhy.autolayout.attr;
import android.view.View;
/**
* Created by zhy on 15/12/5.
*/
public class PaddingBottomAttr extends AutoAttr
{
public PaddingBottomAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.PADDING_BOTTOM;
}
@Override
protected boolean defaultBaseWidth()
{
return false;
}
@Override
protected void execute(View view, int val)
{
int l = view.getPaddingLeft();
int t = view.getPaddingTop();
int r = view.getPaddingRight();
int b = val;
view.setPadding(l, t, r, b);
}
public static PaddingBottomAttr generate(int val, int baseFlag)
{
PaddingBottomAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new PaddingBottomAttr(val, Attrs.PADDING_BOTTOM, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new PaddingBottomAttr(val, 0, Attrs.PADDING_BOTTOM);
break;
case AutoAttr.BASE_DEFAULT:
attr = new PaddingBottomAttr(val, 0, 0);
break;
}
return attr;
}
}

@ -0,0 +1,56 @@
package com.zhy.autolayout.attr;
import android.view.View;
/**
* Created by zhy on 15/12/5.
*/
public class PaddingLeftAttr extends AutoAttr
{
public PaddingLeftAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.PADDING_LEFT;
}
@Override
protected boolean defaultBaseWidth()
{
return true;
}
@Override
protected void execute(View view, int val)
{
int l = val;
int t = view.getPaddingTop();
int r = view.getPaddingRight();
int b = view.getPaddingBottom();
view.setPadding(l, t, r, b);
}
public static PaddingLeftAttr generate(int val, int baseFlag)
{
PaddingLeftAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new PaddingLeftAttr(val, Attrs.PADDING_LEFT, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new PaddingLeftAttr(val, 0, Attrs.PADDING_LEFT);
break;
case AutoAttr.BASE_DEFAULT:
attr = new PaddingLeftAttr(val, 0, 0);
break;
}
return attr;
}
}

@ -0,0 +1,56 @@
package com.zhy.autolayout.attr;
import android.view.View;
/**
* Created by zhy on 15/12/5.
*/
public class PaddingRightAttr extends AutoAttr
{
public PaddingRightAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.PADDING_RIGHT;
}
@Override
protected boolean defaultBaseWidth()
{
return true;
}
@Override
protected void execute(View view, int val)
{
int l = view.getPaddingLeft();
int t = view.getPaddingTop();
int r = val;
int b = view.getPaddingBottom();
view.setPadding(l, t, r, b);
}
public static PaddingRightAttr generate(int val, int baseFlag)
{
PaddingRightAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new PaddingRightAttr(val, Attrs.PADDING_RIGHT, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new PaddingRightAttr(val, 0, Attrs.PADDING_RIGHT);
break;
case AutoAttr.BASE_DEFAULT:
attr = new PaddingRightAttr(val, 0, 0);
break;
}
return attr;
}
}

@ -0,0 +1,54 @@
package com.zhy.autolayout.attr;
import android.view.View;
/**
* Created by zhy on 15/12/5.
*/
public class PaddingTopAttr extends AutoAttr
{
public PaddingTopAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.PADDING_TOP;
}
@Override
protected boolean defaultBaseWidth()
{
return false;
}
@Override
protected void execute(View view, int val)
{
int l = view.getPaddingLeft();
int t = val;
int r = view.getPaddingRight();
int b = view.getPaddingBottom();
view.setPadding(l, t, r, b);
}
public static PaddingTopAttr generate(int val, int baseFlag)
{
PaddingTopAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new PaddingTopAttr(val, Attrs.PADDING_TOP, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new PaddingTopAttr(val, 0, Attrs.PADDING_TOP);
break;
case AutoAttr.BASE_DEFAULT:
attr = new PaddingTopAttr(val, 0, 0);
break;
}
return attr;
}
}

@ -0,0 +1,58 @@
package com.zhy.autolayout.attr;
import android.util.TypedValue;
import android.view.View;
import android.widget.TextView;
/**
* Created by zhy on 15/12/4.
*/
public class TextSizeAttr extends AutoAttr
{
public TextSizeAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.TEXTSIZE;
}
@Override
protected boolean defaultBaseWidth()
{
return false;
}
@Override
protected void execute(View view, int val)
{
if (!(view instanceof TextView))
return;
((TextView) view).setIncludeFontPadding(false);
((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_PX, val);
}
public static TextSizeAttr generate(int val, int baseFlag)
{
TextSizeAttr attr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
attr = new TextSizeAttr(val, Attrs.TEXTSIZE, 0);
break;
case AutoAttr.BASE_HEIGHT:
attr = new TextSizeAttr(val, 0, Attrs.TEXTSIZE);
break;
case AutoAttr.BASE_DEFAULT:
attr = new TextSizeAttr(val, 0, 0);
break;
}
return attr;
}
}

@ -0,0 +1,53 @@
package com.zhy.autolayout.attr;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by zhy on 15/12/5.
*/
public class WidthAttr extends AutoAttr
{
public WidthAttr(int pxVal, int baseWidth, int baseHeight)
{
super(pxVal, baseWidth, baseHeight);
}
@Override
protected int attrVal()
{
return Attrs.WIDTH;
}
@Override
protected boolean defaultBaseWidth()
{
return true;
}
@Override
protected void execute(View view, int val)
{
ViewGroup.LayoutParams lp = view.getLayoutParams();
lp.width = val;
}
public static WidthAttr generate(int val, int baseFlag)
{
WidthAttr widthAttr = null;
switch (baseFlag)
{
case AutoAttr.BASE_WIDTH:
widthAttr = new WidthAttr(val, Attrs.WIDTH, 0);
break;
case AutoAttr.BASE_HEIGHT:
widthAttr = new WidthAttr(val, 0, Attrs.WIDTH);
break;
case AutoAttr.BASE_DEFAULT:
widthAttr = new WidthAttr(val, 0, 0);
break;
}
return widthAttr;
}
}

@ -0,0 +1,111 @@
package com.zhy.autolayout.config;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.zhy.autolayout.utils.L;
import com.zhy.autolayout.utils.ScreenUtils;
/**
* Created by zhy on 15/11/18.
*/
public class AutoLayoutConifg
{
private static AutoLayoutConifg sIntance = new AutoLayoutConifg();
private static final String KEY_DESIGN_WIDTH = "design_width";
private static final String KEY_DESIGN_HEIGHT = "design_height";
private int mScreenWidth;
private int mScreenHeight;
private int mDesignWidth;
private int mDesignHeight;
private boolean useDeviceSize;
private AutoLayoutConifg()
{
}
public void checkParams()
{
if (mDesignHeight <= 0 || mDesignWidth <= 0)
{
throw new RuntimeException(
"you must set " + KEY_DESIGN_WIDTH + " and " + KEY_DESIGN_HEIGHT + " in your manifest file.");
}
}
public AutoLayoutConifg useDeviceSize()
{
useDeviceSize = true;
return this;
}
public static AutoLayoutConifg getInstance()
{
return sIntance;
}
public int getScreenWidth()
{
return mScreenWidth;
}
public int getScreenHeight()
{
return mScreenHeight;
}
public int getDesignWidth()
{
return mDesignWidth;
}
public int getDesignHeight()
{
return mDesignHeight;
}
public void init(Context context)
{
getMetaData(context);
int[] screenSize = ScreenUtils.getScreenSize(context, useDeviceSize);
mScreenWidth = screenSize[0];
mScreenHeight = screenSize[1];
L.e(" screenWidth =" + mScreenWidth + " ,screenHeight = " + mScreenHeight);
}
private void getMetaData(Context context)
{
PackageManager packageManager = context.getPackageManager();
ApplicationInfo applicationInfo;
try
{
applicationInfo = packageManager.getApplicationInfo(context
.getPackageName(), PackageManager.GET_META_DATA);
if (applicationInfo != null && applicationInfo.metaData != null)
{
mDesignWidth = (int) applicationInfo.metaData.get(KEY_DESIGN_WIDTH);
mDesignHeight = (int) applicationInfo.metaData.get(KEY_DESIGN_HEIGHT);
}
} catch (PackageManager.NameNotFoundException e)
{
throw new RuntimeException(
"you must set " + KEY_DESIGN_WIDTH + " and " + KEY_DESIGN_HEIGHT + " in your manifest file.", e);
}
L.e(" designWidth =" + mDesignWidth + " , designHeight = " + mDesignHeight);
}
}

@ -0,0 +1,9 @@
package com.zhy.autolayout.config;
/**
* Created by zhy on 15/12/5.
* Activity稿
*/
public interface UseLandscape
{
}

@ -0,0 +1,233 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zhy.autolayout.utils;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.zhy.autolayout.AutoLayoutInfo;
import com.zhy.autolayout.R;
import com.zhy.autolayout.attr.HeightAttr;
import com.zhy.autolayout.attr.MarginAttr;
import com.zhy.autolayout.attr.MarginBottomAttr;
import com.zhy.autolayout.attr.MarginLeftAttr;
import com.zhy.autolayout.attr.MarginRightAttr;
import com.zhy.autolayout.attr.MarginTopAttr;
import com.zhy.autolayout.attr.MaxHeightAttr;
import com.zhy.autolayout.attr.MaxWidthAttr;
import com.zhy.autolayout.attr.MinHeightAttr;
import com.zhy.autolayout.attr.MinWidthAttr;
import com.zhy.autolayout.attr.PaddingAttr;
import com.zhy.autolayout.attr.PaddingBottomAttr;
import com.zhy.autolayout.attr.PaddingLeftAttr;
import com.zhy.autolayout.attr.PaddingRightAttr;
import com.zhy.autolayout.attr.PaddingTopAttr;
import com.zhy.autolayout.attr.TextSizeAttr;
import com.zhy.autolayout.attr.WidthAttr;
import com.zhy.autolayout.config.AutoLayoutConifg;
public class AutoLayoutHelper
{
private final ViewGroup mHost;
private static final int[] LL = new int[]
{ //
android.R.attr.textSize,
android.R.attr.padding,//
android.R.attr.paddingLeft,//
android.R.attr.paddingTop,//
android.R.attr.paddingRight,//
android.R.attr.paddingBottom,//
android.R.attr.layout_width,//
android.R.attr.layout_height,//
android.R.attr.layout_margin,//
android.R.attr.layout_marginLeft,//
android.R.attr.layout_marginTop,//
android.R.attr.layout_marginRight,//
android.R.attr.layout_marginBottom,//
android.R.attr.maxWidth,//
android.R.attr.maxHeight,//
android.R.attr.minWidth,//
android.R.attr.minHeight,//16843072
};
private static final int INDEX_TEXT_SIZE = 0;
private static final int INDEX_PADDING = 1;
private static final int INDEX_PADDING_LEFT = 2;
private static final int INDEX_PADDING_TOP = 3;
private static final int INDEX_PADDING_RIGHT = 4;
private static final int INDEX_PADDING_BOTTOM = 5;
private static final int INDEX_WIDTH = 6;
private static final int INDEX_HEIGHT = 7;
private static final int INDEX_MARGIN = 8;
private static final int INDEX_MARGIN_LEFT = 9;
private static final int INDEX_MARGIN_TOP = 10;
private static final int INDEX_MARGIN_RIGHT = 11;
private static final int INDEX_MARGIN_BOTTOM = 12;
private static final int INDEX_MAX_WIDTH = 13;
private static final int INDEX_MAX_HEIGHT = 14;
private static final int INDEX_MIN_WIDTH = 15;
private static final int INDEX_MIN_HEIGHT = 16;
/**
* move to other place?
*/
private static AutoLayoutConifg mAutoLayoutConifg;
public AutoLayoutHelper(ViewGroup host)
{
mHost = host;
if (mAutoLayoutConifg == null)
{
initAutoLayoutConfig(host);
}
}
private void initAutoLayoutConfig(ViewGroup host)
{
mAutoLayoutConifg = AutoLayoutConifg.getInstance();
mAutoLayoutConifg.init(host.getContext());
}
public void adjustChildren()
{
AutoLayoutConifg.getInstance().checkParams();
for (int i = 0, n = mHost.getChildCount(); i < n; i++)
{
View view = mHost.getChildAt(i);
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params instanceof AutoLayoutParams)
{
AutoLayoutInfo info =
((AutoLayoutParams) params).getAutoLayoutInfo();
if (info != null)
{
info.fillAttrs(view);
}
}
}
}
public static AutoLayoutInfo getAutoLayoutInfo(Context context,
AttributeSet attrs)
{
AutoLayoutInfo info = new AutoLayoutInfo();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoLayout_Layout);
int baseWidth = a.getInt(R.styleable.AutoLayout_Layout_layout_auto_basewidth, 0);
int baseHeight = a.getInt(R.styleable.AutoLayout_Layout_layout_auto_baseheight, 0);
a.recycle();
TypedArray array = context.obtainStyledAttributes(attrs, LL);
int n = array.getIndexCount();
for (int i = 0; i < n; i++)
{
int index = array.getIndex(i);
// String val = array.getString(index);
// if (!isPxVal(val)) continue;
if (!DimenUtils.isPxVal(array.peekValue(index))) continue;
int pxVal = 0;
try
{
pxVal = array.getDimensionPixelOffset(index, 0);
} catch (Exception ignore)//not dimension
{
continue;
}
switch (index)
{
case INDEX_TEXT_SIZE:
info.addAttr(new TextSizeAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_PADDING:
info.addAttr(new PaddingAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_PADDING_LEFT:
info.addAttr(new PaddingLeftAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_PADDING_TOP:
info.addAttr(new PaddingTopAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_PADDING_RIGHT:
info.addAttr(new PaddingRightAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_PADDING_BOTTOM:
info.addAttr(new PaddingBottomAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_WIDTH:
info.addAttr(new WidthAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_HEIGHT:
info.addAttr(new HeightAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_MARGIN:
info.addAttr(new MarginAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_MARGIN_LEFT:
info.addAttr(new MarginLeftAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_MARGIN_TOP:
info.addAttr(new MarginTopAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_MARGIN_RIGHT:
info.addAttr(new MarginRightAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_MARGIN_BOTTOM:
info.addAttr(new MarginBottomAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_MAX_WIDTH:
info.addAttr(new MaxWidthAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_MAX_HEIGHT:
info.addAttr(new MaxHeightAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_MIN_WIDTH:
info.addAttr(new MinWidthAttr(pxVal, baseWidth, baseHeight));
break;
case INDEX_MIN_HEIGHT:
info.addAttr(new MinHeightAttr(pxVal, baseWidth, baseHeight));
break;
}
}
array.recycle();
L.e(" getAutoLayoutInfo " + info.toString());
return info;
}
public interface AutoLayoutParams
{
AutoLayoutInfo getAutoLayoutInfo();
}
}

@ -0,0 +1,151 @@
package com.zhy.autolayout.utils;
import android.view.View;
import com.zhy.autolayout.AutoLayoutInfo;
import com.zhy.autolayout.R;
import com.zhy.autolayout.attr.Attrs;
import com.zhy.autolayout.attr.AutoAttr;
import com.zhy.autolayout.config.AutoLayoutConifg;
/**
* Created by zhy on 15/12/4.
*/
public class AutoUtils
{
/**
* viewLayoutParamswidthheight
*
* @param view
*/
public static void auto(View view)
{
autoSize(view);
autoPadding(view);
autoMargin(view);
autoTextSize(view, AutoAttr.BASE_DEFAULT);
}
/**
* @param view
* @param attrs #Attrs.WIDTH|Attrs.HEIGHT
* @param base AutoAttr.BASE_WIDTH|AutoAttr.BASE_HEIGHT|AutoAttr.BASE_DEFAULT
*/
public static void auto(View view, int attrs, int base)
{
AutoLayoutInfo autoLayoutInfo = AutoLayoutInfo.getAttrFromView(view, attrs, base);
if (autoLayoutInfo != null)
autoLayoutInfo.fillAttrs(view);
}
public static void autoTextSize(View view)
{
auto(view, Attrs.TEXTSIZE, AutoAttr.BASE_DEFAULT);
}
public static void autoTextSize(View view, int base)
{
auto(view, Attrs.TEXTSIZE, base);
}
public static void autoMargin(View view)
{
auto(view, Attrs.MARGIN, AutoAttr.BASE_DEFAULT);
}
public static void autoMargin(View view, int base)
{
auto(view, Attrs.MARGIN, base);
}
public static void autoPadding(View view)
{
auto(view, Attrs.PADDING, AutoAttr.BASE_DEFAULT);
}
public static void autoPadding(View view, int base)
{
auto(view, Attrs.PADDING, base);
}
public static void autoSize(View view)
{
auto(view, Attrs.WIDTH | Attrs.HEIGHT, AutoAttr.BASE_DEFAULT);
}
public static void autoSize(View view, int base)
{
auto(view, Attrs.WIDTH | Attrs.HEIGHT, base);
}
public static boolean autoed(View view)
{
Object tag = view.getTag(R.id.id_tag_autolayout_size);
if (tag != null) return true;
view.setTag(R.id.id_tag_autolayout_size, "Just Identify");
return false;
}
public static float getPercentWidth1px()
{
int screenWidth = AutoLayoutConifg.getInstance().getScreenWidth();
int designWidth = AutoLayoutConifg.getInstance().getDesignWidth();
return 1.0f * screenWidth / designWidth;
}
public static float getPercentHeight1px()
{
int screenHeight = AutoLayoutConifg.getInstance().getScreenHeight();
int designHeight = AutoLayoutConifg.getInstance().getDesignHeight();
return 1.0f * screenHeight / designHeight;
}
public static int getPercentWidthSize(int val)
{
int screenWidth = AutoLayoutConifg.getInstance().getScreenWidth();
int designWidth = AutoLayoutConifg.getInstance().getDesignWidth();
return (int) (val * 1.0f / designWidth * screenWidth);
}
public static int getPercentWidthSizeBigger(int val)
{
int screenWidth = AutoLayoutConifg.getInstance().getScreenWidth();
int designWidth = AutoLayoutConifg.getInstance().getDesignWidth();
int res = val * screenWidth;
if (res % designWidth == 0)
{
return res / designWidth;
} else
{
return res / designWidth + 1;
}
}
public static int getPercentHeightSizeBigger(int val)
{
int screenHeight = AutoLayoutConifg.getInstance().getScreenHeight();
int designHeight = AutoLayoutConifg.getInstance().getDesignHeight();
int res = val * screenHeight;
if (res % designHeight == 0)
{
return res / designHeight;
} else
{
return res / designHeight + 1;
}
}
public static int getPercentHeightSize(int val)
{
int screenHeight = AutoLayoutConifg.getInstance().getScreenHeight();
int designHeight = AutoLayoutConifg.getInstance().getDesignHeight();
return (int) (val * 1.0f / designHeight * screenHeight);
}
}

@ -0,0 +1,24 @@
package com.zhy.autolayout.utils;
import android.util.TypedValue;
/**
* Created by zhy on 16/3/3.
*/
public class DimenUtils
{
private static int getComplexUnit(int data)
{
return TypedValue.COMPLEX_UNIT_MASK & (data >> TypedValue.COMPLEX_UNIT_SHIFT);
}
public static boolean isPxVal(TypedValue val)
{
if (val != null && val.type == TypedValue.TYPE_DIMENSION &&
getComplexUnit(val.data) == TypedValue.COMPLEX_UNIT_PX)
{
return true;
}
return false;
}
}

@ -0,0 +1,22 @@
package com.zhy.autolayout.utils;
import android.util.Log;
/**
* Created by zhy on 15/11/18.
*/
public class L
{
public static boolean debug = false;
private static final String TAG = "AUTO_LAYOUT";
public static void e(String msg)
{
if (debug)
{
Log.e(TAG, msg);
}
}
}

@ -0,0 +1,82 @@
package com.zhy.autolayout.utils;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;
/**
* Created by zhy on 15/12/4.<br/>
* form http://stackoverflow.com/questions/1016896/get-screen-dimensions-in-pixels/15699681#15699681
*/
public class ScreenUtils
{
public static int getStatusBarHeight(Context context)
{
int result = 0;
try
{
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
{
result = context.getResources().getDimensionPixelSize(resourceId);
}
} catch (Resources.NotFoundException e)
{
e.printStackTrace();
}
return result;
}
public static int[] getScreenSize(Context context, boolean useDeviceSize)
{
int[] size = new int[2];
WindowManager w = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display d = w.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
d.getMetrics(metrics);
// since SDK_INT = 1;
int widthPixels = metrics.widthPixels;
int heightPixels = metrics.heightPixels;
if (!useDeviceSize)
{
size[0] = widthPixels;
size[1] = heightPixels - getStatusBarHeight(context);
return size;
}
// includes window decorations (statusbar bar/menu bar)
if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 17)
try
{
widthPixels = (Integer) Display.class.getMethod("getRawWidth").invoke(d);
heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(d);
} catch (Exception ignored)
{
}
// includes window decorations (statusbar bar/menu bar)
if (Build.VERSION.SDK_INT >= 17)
try
{
Point realSize = new Point();
Display.class.getMethod("getRealSize", Point.class).invoke(d, realSize);
widthPixels = realSize.x;
heightPixels = realSize.y;
} catch (Exception ignored)
{
}
size[0] = widthPixels;
size[1] = heightPixels;
return size;
}
}

@ -0,0 +1,233 @@
package com.zhy.autolayout.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.zhy.autolayout.AutoLayoutInfo;
import com.zhy.autolayout.R;
import com.zhy.autolayout.utils.AutoLayoutHelper;
import com.zhy.autolayout.utils.AutoUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Created by zhy on 15/12/10.
*
* //do not use
*/
public class MetroLayout extends ViewGroup
{
private final AutoLayoutHelper mHelper = new AutoLayoutHelper(this);
private static class MetroBlock
{
int left;
int top;
int width;
}
private List<MetroBlock> mAvailablePos = new ArrayList<>();
private int mDivider;
public MetroLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MetroLayout);
mDivider = a.getDimensionPixelOffset(R.styleable.MetroLayout_metro_divider, 0);
mDivider = AutoUtils.getPercentWidthSizeBigger(mDivider);
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (true)
randomColor();
if (!isInEditMode())
mHelper.adjustChildren();
measureChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void randomColor()
{
Random r = new Random(255);
for (int i = 0, n = getChildCount(); i < n; i++)
{
View v = getChildAt(i);
v.setBackgroundColor(Color.argb(100, r.nextInt(), r.nextInt(), r.nextInt()));
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
initAvailablePosition();
int left = 0;
int top = 0;
int divider = mDivider;
for (int i = 0, n = getChildCount(); i < n; i++)
{
View v = getChildAt(i);
if (v.getVisibility() == View.GONE) continue;
MetroBlock newPos = findAvailablePos(v);
left = newPos.left;
top = newPos.top;
int childWidth = v.getMeasuredWidth();
int childHeight = v.getMeasuredHeight();
int right = left + childWidth;
int bottom = top + childHeight;
v.layout(left, top, right, bottom);
if (childWidth + divider < newPos.width)
{
newPos.left += childWidth + divider;
newPos.width -= childWidth + divider;
} else
{
mAvailablePos.remove(newPos);
}
MetroBlock p = new MetroBlock();
p.left = left;
p.top = bottom + divider;
p.width = childWidth;
mAvailablePos.add(p);
mergeAvailablePosition();
}
}
private void mergeAvailablePosition()
{
if (mAvailablePos.size() <= 1) return;
List<MetroBlock> needRemoveBlocks = new ArrayList<>();
MetroBlock one = mAvailablePos.get(0);
MetroBlock two = mAvailablePos.get(1);
for (int i = 1, n = mAvailablePos.size(); i < n - 1; i++)
{
if (one.top == two.top)
{
one.width = one.width + two.width;
needRemoveBlocks.add(one);
two.left = one.left;
two = mAvailablePos.get(i + 1);
} else
{
one = mAvailablePos.get(i);
two = mAvailablePos.get(i + 1);
}
}
mAvailablePos.removeAll(needRemoveBlocks);
}
private void initAvailablePosition()
{
mAvailablePos.clear();
MetroBlock first = new MetroBlock();
first.left = getPaddingLeft();
first.top = getPaddingTop();
first.width = getMeasuredWidth();
mAvailablePos.add(first);
}
private MetroBlock findAvailablePos(View view)
{
MetroBlock p = new MetroBlock();
if (mAvailablePos.size() == 0)
{
p.left = getPaddingLeft();
p.top = getPaddingTop();
p.width = getMeasuredWidth();
return p;
}
int min = mAvailablePos.get(0).top;
MetroBlock minHeightPos = mAvailablePos.get(0);
for (MetroBlock _p : mAvailablePos)
{
if (_p.top < min)
{
min = _p.top;
minHeightPos = _p;
}
}
return minHeightPos;
}
@Override
public MetroLayout.LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new LayoutParams(getContext(), attrs);
}
public static class LayoutParams extends ViewGroup.MarginLayoutParams
implements AutoLayoutHelper.AutoLayoutParams
{
private AutoLayoutInfo mAutoLayoutInfo;
public LayoutParams(Context c, AttributeSet attrs)
{
super(c, attrs);
mAutoLayoutInfo = AutoLayoutHelper.getAutoLayoutInfo(c, attrs);
}
public LayoutParams(int width, int height)
{
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source)
{
super(source);
}
public LayoutParams(MarginLayoutParams source)
{
super(source);
}
public LayoutParams(LayoutParams source)
{
this((ViewGroup.LayoutParams) source);
mAutoLayoutInfo = source.mAutoLayoutInfo;
}
@Override
public AutoLayoutInfo getAutoLayoutInfo()
{
return mAutoLayoutInfo;
}
}
}

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AutoLayout_Layout">
<attr name="layout_auto_basewidth">
<flag name="width" value="1"></flag>
<flag name="height" value="2"></flag>
<flag name="textSize" value="4"></flag>
<flag name="padding" value="8"></flag>
<flag name="margin" value="16"></flag>
<flag name="marginLeft" value="32"></flag>
<flag name="marginTop" value="64"></flag>
<flag name="marginRight" value="128"></flag>
<flag name="marginBottom" value="256"></flag>
<flag name="paddingLeft" value="512"></flag>
<flag name="paddingTop" value="1024"></flag>
<flag name="paddingRight" value="2048"></flag>
<flag name="paddingBottom" value="4096"></flag>
</attr>
<attr name="layout_auto_baseheight">
<flag name="width" value="1"></flag>
<flag name="height" value="2"></flag>
<flag name="textSize" value="4"></flag>
<flag name="padding" value="8"></flag>
<flag name="margin" value="16"></flag>
<flag name="marginLeft" value="32"></flag>
<flag name="marginTop" value="64"></flag>
<flag name="marginRight" value="128"></flag>
<flag name="marginBottom" value="256"></flag>
<flag name="paddingLeft" value="512"></flag>
<flag name="paddingTop" value="1024"></flag>
<flag name="paddingRight" value="2048"></flag>
<flag name="paddingBottom" value="4096"></flag>
<flag name="minWidth" value="8192"></flag>
<flag name="maxWidth" value="16384"></flag>
<flag name="minHeight" value="32768"></flag>
<flag name="maxHeight" value="65536"></flag>
</attr>
</declare-styleable>
<declare-styleable name="MetroLayout">
<attr name="metro_divider" format="dimension"></attr>
</declare-styleable>
</resources>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="id_tag_autolayout_size" type="id"></item>
<item name="id_tag_autolayout_padding" type="id"></item>
<item name="id_tag_autolayout_margin" type="id"></item>
</resources>

@ -0,0 +1,3 @@
<resources>
<string name="app_name">autolayout</string>
</resources>

@ -23,7 +23,7 @@ kotlin.code.style=official
#android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true
#android.defaults.buildfeatures.buildconfig=true
android.enableJetifier=true
android.nonFinalResIds=false
android.nonTransitiveRClass=false

@ -0,0 +1,18 @@
apply plugin: 'com.android.library'
android {
compileSdk 34
defaultConfig {
minSdk 24
targetSdk 34
versionName "1.5.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
namespace 'org.litepal'
}

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/tony/Android/Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

@ -0,0 +1,13 @@
package org.litepal;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

@ -0,0 +1,170 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import org.litepal.parser.LitePalAttr;
import org.litepal.parser.LitePalConfig;
import org.litepal.parser.LitePalParser;
import org.litepal.tablemanager.Connector;
import org.litepal.util.BaseUtility;
import org.litepal.util.Const;
import org.litepal.util.SharedUtil;
import java.io.File;
/**
* LitePal is an Android library that allows developers to use SQLite database extremely easy.
* You can initialized it by calling {@link #initialize(Context)} method to make LitePal ready to
* work. Also you can switch the using database by calling {@link #use(LitePalDB)} and {@link #useDefault()}
* methods.
*
* @author Tony Green
* @since 1.4
*/
public class LitePal {
private static Handler handler = new Handler(Looper.getMainLooper());
/**
* Initialize to make LitePal ready to work. If you didn't configure LitePalApplication
* in the AndroidManifest.xml, make sure you call this method as soon as possible. In
* Application's onCreate() method will be fine.
*
* @param context
* Application context.
*/
public static void initialize(Context context) {
LitePalApplication.sContext = context;
}
/**
* Get a writable SQLiteDatabase.
*
* @return A writable SQLiteDatabase instance
*/
public static SQLiteDatabase getDatabase() {
return Connector.getDatabase();
}
/**
* Get the main thread handler. You don't need this method. It's used by framework only.
* @return Main thread handler.
*/
public static Handler getHandler() {
return handler;
}
/**
* Switch the using database to the one specified by parameter.
* @param litePalDB
* The database to switch to.
*/
public static void use(LitePalDB litePalDB) {
LitePalAttr litePalAttr = LitePalAttr.getInstance();
litePalAttr.setDbName(litePalDB.getDbName());
litePalAttr.setVersion(litePalDB.getVersion());
litePalAttr.setStorage(litePalDB.isExternalStorage() ? "external" : "internal");
litePalAttr.setClassNames(litePalDB.getClassNames());
// set the extra key name only when use database other than default or litepal.xml not exists
if (!isDefaultDatabase(litePalDB.getDbName())) {
litePalAttr.setExtraKeyName(litePalDB.getDbName());
litePalAttr.setCases("lower");
}
Connector.clearLitePalOpenHelperInstance();
}
/**
* Switch the using database to default with configuration by litepal.xml.
*/
public static void useDefault() {
LitePalAttr.clearInstance();
Connector.clearLitePalOpenHelperInstance();
}
/**
* Delete the specified database.
* @param dbName
* Name of database to delete.
* @return True if delete success, false otherwise.
*/
public static boolean deleteDatabase(String dbName) {
if (!TextUtils.isEmpty(dbName)) {
if (!dbName.endsWith(Const.Config.DB_NAME_SUFFIX)) {
dbName = dbName + Const.Config.DB_NAME_SUFFIX;
}
File dbFile = LitePalApplication.getContext().getDatabasePath(dbName);
if (dbFile.exists()) {
boolean result = dbFile.delete();
if (result) {
removeVersionInSharedPreferences(dbName);
Connector.clearLitePalOpenHelperInstance();
}
return result;
}
String path = LitePalApplication.getContext().getExternalFilesDir("") + "/databases/";
dbFile = new File(path + dbName);
boolean result = dbFile.delete();
if (result) {
removeVersionInSharedPreferences(dbName);
Connector.clearLitePalOpenHelperInstance();
}
return result;
}
return false;
}
/**
* Remove the database version in SharedPreferences file.
* @param dbName
*/
private static void removeVersionInSharedPreferences(String dbName) {
if (isDefaultDatabase(dbName)) {
SharedUtil.removeVersion(null);
} else {
SharedUtil.removeVersion(dbName);
}
}
/**
* Check the dbName is default database or not. If it's same as dbName in litepal.xml, then it is
* default database.
* @param dbName
* Name of database to check.
* @return True if it's default database, false otherwise.
*/
private static boolean isDefaultDatabase(String dbName) {
if (BaseUtility.isLitePalXMLExists()) {
if (!dbName.endsWith(Const.Config.DB_NAME_SUFFIX)) {
dbName = dbName + Const.Config.DB_NAME_SUFFIX;
}
LitePalConfig config = LitePalParser.parseLitePalConfiguration();
String defaultDbName = config.getDbName();
if (!defaultDbName.endsWith(Const.Config.DB_NAME_SUFFIX)) {
defaultDbName = defaultDbName + Const.Config.DB_NAME_SUFFIX;
}
return dbName.equalsIgnoreCase(defaultDbName);
}
return false;
}
}

@ -0,0 +1,79 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal;
import org.litepal.exceptions.GlobalException;
import android.app.Application;
import android.content.Context;
/**
* Base class of LitePal to make things easier when developers need to use
* context. When you need context, just use
* <b>LitePalApplication.getContext()</b>. To make this function work, you need
* to configure your AndroidManifest.xml. Specifying
* <b>"org.litepal.LitePalApplication"</b> as the application name in your
* &lt;application&gt; tag to enable LitePal get the context. Of course if you
* need to write your own Application class, LitePal can still live with that.
* But just remember make your own Application class inherited from
* LitePalApplication instead of inheriting from Application directly. This can
* make all things work without side effects. <br>
* Besides if you don't want use the above way, you can also call the LitePal.initialize(Context)
* method to do the same job. Just remember call this method as early as possible, in Application's onCreate()
* method will be fine.
*
* @author Tony Green
* @since 1.0
*/
public class LitePalApplication extends Application {
/**
* Global application context.
*/
static Context sContext;
/**
* Construct of LitePalApplication. Initialize application context.
*/
public LitePalApplication() {
sContext = this;
}
/**
* Deprecated. Use {@link LitePal#initialize(Context)} instead.
* @param context
* Application context.
*/
@Deprecated
public static void initialize(Context context) {
sContext = context;
}
/**
* Get the global application context.
*
* @return Application context.
* @throws org.litepal.exceptions.GlobalException
*/
public static Context getContext() {
if (sContext == null) {
throw new GlobalException(GlobalException.APPLICATION_CONTEXT_IS_NULL);
}
return sContext;
}
}

@ -0,0 +1,693 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal;
import org.litepal.annotation.Column;
import org.litepal.crud.DataSupport;
import org.litepal.crud.model.AssociationsInfo;
import org.litepal.exceptions.DatabaseGenerateException;
import org.litepal.parser.LitePalAttr;
import org.litepal.tablemanager.model.AssociationsModel;
import org.litepal.tablemanager.model.ColumnModel;
import org.litepal.tablemanager.model.GenericModel;
import org.litepal.tablemanager.model.TableModel;
import org.litepal.tablemanager.typechange.BlobOrm;
import org.litepal.tablemanager.typechange.BooleanOrm;
import org.litepal.tablemanager.typechange.DateOrm;
import org.litepal.tablemanager.typechange.DecimalOrm;
import org.litepal.tablemanager.typechange.NumericOrm;
import org.litepal.tablemanager.typechange.OrmChange;
import org.litepal.tablemanager.typechange.TextOrm;
import org.litepal.util.BaseUtility;
import org.litepal.util.Const;
import org.litepal.util.DBUtility;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Base class of all the LitePal components. If each component need to
* interactive with other components or they have some same logic with duplicate
* codes, LitePalBase may be the solution.
*
* @author Tony Green
* @since 1.1
*/
public abstract class LitePalBase {
public static final String TAG = "LitePalBase";
/**
* Action to get associations.
*/
private static final int GET_ASSOCIATIONS_ACTION = 1;
/**
* Action to get association info.
*/
private static final int GET_ASSOCIATION_INFO_ACTION = 2;
/**
* All the supporting mapping types currently in the array.
*/
private OrmChange[] typeChangeRules = { new NumericOrm(), new TextOrm(), new BooleanOrm(),
new DecimalOrm(), new DateOrm(), new BlobOrm()};
/**
* This is map of class name to fields list. Indicates that each class has which supported fields.
*/
private Map<String, List<Field>> classFieldsMap = new HashMap<String, List<Field>>();
/**
* This is map of class name to generic fields list. Indicates that each class has which supported generic fields.
*/
private Map<String, List<Field>> classGenericFieldsMap = new HashMap<String, List<Field>>();
/**
* The collection contains all association models.
*/
private Collection<AssociationsModel> mAssociationModels;
/**
* The collection contains all association info.
*/
private Collection<AssociationsInfo> mAssociationInfos;
/**
* The collection contains all generic models.
*/
private Collection<GenericModel> mGenericModels;
/**
* This method is used to get the table model by the class name passed
* in. The principle to generate table model is that each field in the class
* with non-static modifier and has a type among int/Integer, long/Long,
* short/Short, float/Float, double/Double, char/Character, boolean/Boolean
* or String, would generate a column with same name as corresponding field.
* If users don't want some of the fields map a column, declare an ignore
* annotation with {@link Column#ignore()}.
*
* @param className
* The full name of the class to map in database.
* @return A table model with table name, class name and the map of column
* name and column type.
*/
protected TableModel getTableModel(String className) {
String tableName = DBUtility.getTableNameByClassName(className);
TableModel tableModel = new TableModel();
tableModel.setTableName(tableName);
tableModel.setClassName(className);
List<Field> supportedFields = getSupportedFields(className);
for (Field field : supportedFields) {
ColumnModel columnModel = convertFieldToColumnModel(field);
tableModel.addColumnModel(columnModel);
}
return tableModel;
}
/**
* This method is used to get association models depends on the given class
* name list.
*
* @param classNames
* The names of the classes that want to get their associations.
* @return Collection of association models.
*/
protected Collection<AssociationsModel> getAssociations(List<String> classNames) {
if (mAssociationModels == null) {
mAssociationModels = new HashSet<AssociationsModel>();
}
if (mGenericModels == null) {
mGenericModels = new HashSet<GenericModel>();
}
mAssociationModels.clear();
mGenericModels.clear();
for (String className : classNames) {
analyzeClassFields(className, GET_ASSOCIATIONS_ACTION);
}
return mAssociationModels;
}
/**
* Get all generic models for create generic tables.
* @return All generic models.
*/
protected Collection<GenericModel> getGenericModels() {
return mGenericModels;
}
/**
* Get the association info model by the class name.
*
* @param className
* The class name to introspection.
* @return Collection of association info.
*/
protected Collection<AssociationsInfo> getAssociationInfo(String className) {
if (mAssociationInfos == null) {
mAssociationInfos = new HashSet<AssociationsInfo>();
}
mAssociationInfos.clear();
analyzeClassFields(className, GET_ASSOCIATION_INFO_ACTION);
return mAssociationInfos;
}
/**
* Find all the fields in the class. But not each field is supported to add
* a column to the table. Only the basic data types and String are
* supported. This method will intercept all the types which are not
* supported and return a new list of supported fields.
*
* @param className
* The full name of the class.
* @return A list of supported fields.
*/
protected List<Field> getSupportedFields(String className) {
List<Field> fieldList = classFieldsMap.get(className);
if (fieldList == null) {
List<Field> supportedFields = new ArrayList<Field>();
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new DatabaseGenerateException(DatabaseGenerateException.CLASS_NOT_FOUND + className);
}
recursiveSupportedFields(clazz, supportedFields);
classFieldsMap.put(className, supportedFields);
return supportedFields;
}
return fieldList;
}
/**
* Find all supported generic fields in the class. Supporting rule is in {@link BaseUtility#isGenericTypeSupported(String)}.
* @param className
* The full name of the class.
* @return A list of supported generic fields.
*/
protected List<Field> getSupportedGenericFields(String className) {
List<Field> genericFieldList = classGenericFieldsMap.get(className);
if (genericFieldList == null) {
List<Field> supportedGenericFields = new ArrayList<Field>();
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new DatabaseGenerateException(DatabaseGenerateException.CLASS_NOT_FOUND + className);
}
recursiveSupportedGenericFields(clazz, supportedGenericFields);
classGenericFieldsMap.put(className, supportedGenericFields);
return supportedGenericFields;
}
return genericFieldList;
}
/**
* If the field type implements from List or Set, regard it as a collection.
*
* @param fieldType
* The field type.
* @return True if the field type is collection, false otherwise.
*/
protected boolean isCollection(Class<?> fieldType) {
return isList(fieldType) || isSet(fieldType);
}
/**
* If the field type implements from List, regard it as a list.
*
* @param fieldType
* The field type.
* @return True if the field type is List, false otherwise.
*/
protected boolean isList(Class<?> fieldType) {
return List.class.isAssignableFrom(fieldType);
}
/**
* If the field type implements from Set, regard it as a set.
*
* @param fieldType
* The field type.
* @return True if the field type is Set, false otherwise.
*/
protected boolean isSet(Class<?> fieldType) {
return Set.class.isAssignableFrom(fieldType);
}
/**
* Judge the passed in column is an id column or not. The column named id or
* _id will be considered as id column.
*
* @param columnName
* The name of column.
* @return Return true if it's id column, otherwise return false.
*/
protected boolean isIdColumn(String columnName) {
return "_id".equalsIgnoreCase(columnName) || "id".equalsIgnoreCase(columnName);
}
/**
* If two tables are associated, one table have a foreign key column. The
* foreign key column name will be the associated table name with _id
* appended.
*
* @param associatedTableName
* The associated table name.
* @return The foreign key column name.
*/
protected String getForeignKeyColumnName(String associatedTableName) {
return BaseUtility.changeCase(associatedTableName + "_id");
}
/**
* Get the column type for creating table by field type.
* @param fieldType
* Type of field.
* @return The column type for creating table.
*/
protected String getColumnType(String fieldType) {
String columnType;
for (OrmChange ormChange : typeChangeRules) {
columnType = ormChange.object2Relation(fieldType);
if (columnType != null) {
return columnType;
}
}
return null;
}
/**
* Get the generic type class of List or Set. If there's no generic type of
* List or Set return null.
*
* @param field
* A generic type field.
* @return The generic type of List or Set.
*/
protected Class<?> getGenericTypeClass(Field field) {
Type genericType = field.getGenericType();
if (genericType != null) {
if (genericType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
return (Class<?>) parameterizedType.getActualTypeArguments()[0];
}
}
return null;
}
private void recursiveSupportedFields(Class<?> clazz, List<Field> supportedFields) {
if (clazz == DataSupport.class || clazz == Object.class) {
return;
}
Field[] fields = clazz.getDeclaredFields();
if (fields != null && fields.length > 0) {
for (Field field : fields) {
Column annotation = field.getAnnotation(Column.class);
if (annotation != null && annotation.ignore()) {
continue;
}
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifiers)) {
Class<?> fieldTypeClass = field.getType();
String fieldType = fieldTypeClass.getName();
if (BaseUtility.isFieldTypeSupported(fieldType)) {
supportedFields.add(field);
}
}
}
}
recursiveSupportedFields(clazz.getSuperclass(), supportedFields);
}
private void recursiveSupportedGenericFields(Class<?> clazz, List<Field> supportedGenericFields) {
if (clazz == DataSupport.class || clazz == Object.class) {
return;
}
Field[] fields = clazz.getDeclaredFields();
if (fields != null && fields.length > 0) {
for (Field field : fields) {
Column annotation = field.getAnnotation(Column.class);
if (annotation != null && annotation.ignore()) {
continue;
}
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifiers) && isCollection(field.getType())) {
String genericTypeName = getGenericTypeName(field);
if (BaseUtility.isGenericTypeSupported(genericTypeName)) {
supportedGenericFields.add(field);
}
}
}
}
recursiveSupportedGenericFields(clazz.getSuperclass(), supportedGenericFields);
}
/**
* Introspection of the passed in class. Analyze the fields of current class
* and find out the associations of it.
*
* @param className
* The class name to introspection.
* @param action
* Between {@link org.litepal.LitePalBase#GET_ASSOCIATIONS_ACTION} and
* {@link org.litepal.LitePalBase#GET_ASSOCIATION_INFO_ACTION}
*/
private void analyzeClassFields(String className, int action) {
try {
Class<?> dynamicClass = Class.forName(className);
Field[] fields = dynamicClass.getDeclaredFields();
for (Field field : fields) {
if (isPrivateAndNonPrimitive(field)) {
oneToAnyConditions(className, field, action);
manyToAnyConditions(className, field, action);
}
}
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
throw new DatabaseGenerateException(DatabaseGenerateException.CLASS_NOT_FOUND + className);
}
}
/**
* Judge the field is a private non primitive field or not.
*
* @param field
* The field to judge.
* @return True if the field is <b>private</b> and <b>non primitive</b>,
* false otherwise.
*/
private boolean isPrivateAndNonPrimitive(Field field) {
return Modifier.isPrivate(field.getModifiers()) && !field.getType().isPrimitive();
}
/**
* Deals with one to any association conditions. e.g. Song and Album. An
* album have many songs, and a song belongs to one album. So if there's an
* Album model defined in Song with private modifier, and in Album there's a
* List or Set with generic type of Song and declared as private modifier,
* they are one2many association. If there's no List or Set defined in
* Album, they will become one2one associations. If there's also a Song
* model defined in Album with private modifier, maybe the album just have
* one song, they are one2one association too.
*
* When it's many2one association, it's easy to just simply add a foreign id
* column to the many side model's table. But when it comes to many2many
* association, it can not be done without intermediate join table in
* database. LitePal assumes that this join table's name is the
* concatenation of the two target table names in alphabetical order.
*
* @param className
* Source class name.
* @param field
* A field of source class.
* @param action
* Between {@link org.litepal.LitePalBase#GET_ASSOCIATIONS_ACTION} and
* {@link org.litepal.LitePalBase#GET_ASSOCIATION_INFO_ACTION}
*
* @throws ClassNotFoundException
*/
private void oneToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {
Class<?> fieldTypeClass = field.getType();
// If the mapping list contains the class name
// defined in one class.
if (LitePalAttr.getInstance().getClassNames().contains(fieldTypeClass.getName())) {
Class<?> reverseDynamicClass = Class.forName(fieldTypeClass.getName());
Field[] reverseFields = reverseDynamicClass.getDeclaredFields();
// Look up if there's a reverse association
// definition in the reverse class.
boolean reverseAssociations = false;
// Begin to check the fields of the defined
// class.
for (int i = 0; i < reverseFields.length; i++) {
Field reverseField = reverseFields[i];
if (!Modifier.isStatic(reverseField.getModifiers())) {
Class<?> reverseFieldTypeClass = reverseField.getType();
// If there's the from class name in the
// defined class, they are one2one bidirectional
// associations.
if (className.equals(reverseFieldTypeClass.getName())) {
if (action == GET_ASSOCIATIONS_ACTION) {
addIntoAssociationModelCollection(className, fieldTypeClass.getName(),
fieldTypeClass.getName(), Const.Model.ONE_TO_ONE);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),
fieldTypeClass.getName(), field, reverseField, Const.Model.ONE_TO_ONE);
}
reverseAssociations = true;
}
// If there's the from class Set or List in
// the defined class, they are many2one bidirectional
// associations.
else if (isCollection(reverseFieldTypeClass)) {
String genericTypeName = getGenericTypeName(reverseField);
if (className.equals(genericTypeName)) {
if (action == GET_ASSOCIATIONS_ACTION) {
addIntoAssociationModelCollection(className, fieldTypeClass.getName(),
className, Const.Model.MANY_TO_ONE);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),
className, field, reverseField, Const.Model.MANY_TO_ONE);
}
reverseAssociations = true;
}
}
}
}
// If there's no from class in the defined class, they are
// one2one unidirectional associations.
if (!reverseAssociations) {
if (action == GET_ASSOCIATIONS_ACTION) {
addIntoAssociationModelCollection(className, fieldTypeClass.getName(),
fieldTypeClass.getName(), Const.Model.ONE_TO_ONE);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
addIntoAssociationInfoCollection(className, fieldTypeClass.getName(),
fieldTypeClass.getName(), field, null, Const.Model.ONE_TO_ONE);
}
}
}
}
/**
* Deals with one to any association conditions. e.g. Song and Album. An
* album have many songs, and a song belongs to one album. So if there's an
* Album model defined in Song with private modifier, and in Album there's a
* List or Set with generic type of Song and declared as private modifier,
* they are one2many association. If there's no List or Set defined in
* Album, they will become one2one associations. If there's also a Song
* model defined in Album with private modifier, maybe the album just have
* one song, they are one2one association too.
*
* When it's many2one association, it's easy to just simply add a foreign id
* column to the many side model's table. But when it comes to many2many
* association, it can not be done without intermediate join table in
* database. LitePal assumes that this join table's name is the
* concatenation of the two target table names in alphabetical order.
*
* @param className
* Source class name.
* @param field
* A field of source class.
* @param action
* Between {@link org.litepal.LitePalBase#GET_ASSOCIATIONS_ACTION} and
* {@link org.litepal.LitePalBase#GET_ASSOCIATION_INFO_ACTION}
*
* @throws ClassNotFoundException
*/
private void manyToAnyConditions(String className, Field field, int action) throws ClassNotFoundException {
if (isCollection(field.getType())) {
String genericTypeName = getGenericTypeName(field);
// If the mapping list contains the genericTypeName, begin to check
// this genericTypeName class.
if (LitePalAttr.getInstance().getClassNames().contains(genericTypeName)) {
Class<?> reverseDynamicClass = Class.forName(genericTypeName);
Field[] reverseFields = reverseDynamicClass.getDeclaredFields();
// Look up if there's a reverse association
// definition in the reverse class.
boolean reverseAssociations = false;
for (int i = 0; i < reverseFields.length; i++) {
Field reverseField = reverseFields[i];
// Only map private fields
if (!Modifier.isStatic(reverseField.getModifiers())) {
Class<?> reverseFieldTypeClass = reverseField.getType();
// If there's a from class name defined in the reverse
// class, they are many2one bidirectional
// associations.
if (className.equals(reverseFieldTypeClass.getName())) {
if (action == GET_ASSOCIATIONS_ACTION) {
addIntoAssociationModelCollection(className, genericTypeName,
genericTypeName, Const.Model.MANY_TO_ONE);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName,
field, reverseField, Const.Model.MANY_TO_ONE);
}
reverseAssociations = true;
}
// If there's a List or Set contains from class name
// defined in the reverse class, they are many2many
// association.
else if (isCollection(reverseFieldTypeClass)) {
String reverseGenericTypeName = getGenericTypeName(reverseField);
if (className.equals(reverseGenericTypeName)) {
if (action == GET_ASSOCIATIONS_ACTION) {
addIntoAssociationModelCollection(className, genericTypeName, null,
Const.Model.MANY_TO_MANY);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
addIntoAssociationInfoCollection(className, genericTypeName, null, field,
reverseField, Const.Model.MANY_TO_MANY);
}
reverseAssociations = true;
}
}
}
}
// If there's no from class in the defined class, they
// are many2one unidirectional associations.
if (!reverseAssociations) {
if (action == GET_ASSOCIATIONS_ACTION) {
addIntoAssociationModelCollection(className, genericTypeName,
genericTypeName, Const.Model.MANY_TO_ONE);
} else if (action == GET_ASSOCIATION_INFO_ACTION) {
addIntoAssociationInfoCollection(className, genericTypeName, genericTypeName,
field, null, Const.Model.MANY_TO_ONE);
}
}
} else if(BaseUtility.isGenericTypeSupported(genericTypeName) && action == GET_ASSOCIATIONS_ACTION) {
Column annotation = field.getAnnotation(Column.class);
if (annotation != null && annotation.ignore()) {
return;
}
GenericModel genericModel = new GenericModel();
genericModel.setTableName(DBUtility.getGenericTableName(className, field.getName()));
genericModel.setValueColumnName(DBUtility.convertToValidColumnName(field.getName()));
genericModel.setValueColumnType(getColumnType(genericTypeName));
genericModel.setValueIdColumnName(DBUtility.getGenericValueIdColumnName(className));
mGenericModels.add(genericModel);
}
}
}
/**
* Package a {@link org.litepal.tablemanager.model.AssociationsModel}, and add it into
* {@link #mAssociationModels} Collection.
*
* @param className
* The class name for {@link org.litepal.tablemanager.model.AssociationsModel}.
* @param associatedClassName
* The associated class name for {@link org.litepal.tablemanager.model.AssociationsModel}.
* @param classHoldsForeignKey
* The class which holds foreign key.
* @param associationType
* The association type for {@link org.litepal.tablemanager.model.AssociationsModel}.
*/
private void addIntoAssociationModelCollection(String className, String associatedClassName,
String classHoldsForeignKey, int associationType) {
AssociationsModel associationModel = new AssociationsModel();
associationModel.setTableName(DBUtility.getTableNameByClassName(className));
associationModel.setAssociatedTableName(DBUtility.getTableNameByClassName(associatedClassName));
associationModel.setTableHoldsForeignKey(DBUtility.getTableNameByClassName(classHoldsForeignKey));
associationModel.setAssociationType(associationType);
mAssociationModels.add(associationModel);
}
/**
* Package a {@link org.litepal.crud.model.AssociationsInfo}, and add it into
* {@link #mAssociationInfos} Collection.
*
* @param selfClassName
* The class name of self model.
* @param associatedClassName
* The class name of the class which associated with self class.
* @param classHoldsForeignKey
* The class which holds foreign key.
* @param associateOtherModelFromSelf
* The field of self class to declare has association with other
* class.
* @param associateSelfFromOtherModel
* The field of the associated class to declare has association
* with self class.
* @param associationType
* The association type.
*/
private void addIntoAssociationInfoCollection(String selfClassName, String associatedClassName,
String classHoldsForeignKey, Field associateOtherModelFromSelf,
Field associateSelfFromOtherModel, int associationType) {
AssociationsInfo associationInfo = new AssociationsInfo();
associationInfo.setSelfClassName(selfClassName);
associationInfo.setAssociatedClassName(associatedClassName);
associationInfo.setClassHoldsForeignKey(classHoldsForeignKey);
associationInfo.setAssociateOtherModelFromSelf(associateOtherModelFromSelf);
associationInfo.setAssociateSelfFromOtherModel(associateSelfFromOtherModel);
associationInfo.setAssociationType(associationType);
mAssociationInfos.add(associationInfo);
}
/**
* Get the generic type name of List or Set. If there's no generic type of
* List or Set return null.
*
* @param field
* A generic type field.
* @return The name of generic type of List of Set.
*/
protected String getGenericTypeName(Field field) {
Class<?> genericTypeClass = getGenericTypeClass(field);
if (genericTypeClass != null) {
return genericTypeClass.getName();
}
return null;
}
/**
* Convert a field instance into A ColumnModel instance. ColumnModel can provide information
* when creating table.
* @param field
* A supported field to map into column.
* @return ColumnModel instance contains column information.
*/
private ColumnModel convertFieldToColumnModel(Field field) {
String fieldType = field.getType().getName();
String columnType = getColumnType(fieldType);
boolean nullable = true;
boolean unique = false;
String defaultValue = "";
Column annotation = field.getAnnotation(Column.class);
if (annotation != null) {
nullable = annotation.nullable();
unique = annotation.unique();
defaultValue = annotation.defaultValue();
}
ColumnModel columnModel = new ColumnModel();
columnModel.setColumnName(DBUtility.convertToValidColumnName(field.getName()));
columnModel.setColumnType(columnType);
columnModel.setNullable(nullable);
columnModel.setUnique(unique);
columnModel.setDefaultValue(defaultValue);
return columnModel;
}
}

@ -0,0 +1,129 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal;
import org.litepal.parser.LitePalAttr;
import org.litepal.parser.LitePalConfig;
import org.litepal.parser.LitePalParser;
import java.util.ArrayList;
import java.util.List;
/**
* Configuration of LitePal database. It's similar to litepal.xml configuration, but allows to
* configure database details at runtime. This is very important when comes to support multiple
* databases functionality.
*
* @author Tony Green
* @since 1.4
*/
public class LitePalDB {
/**
* The version of database.
*/
private int version;
/**
* The name of database.
*/
private String dbName;
/**
* Indicates that the database file stores in external storage or not.
*/
private boolean isExternalStorage = false;
/**
* All the model classes that want to map in the database. Each class should
* be given the full name including package name.
*/
private List<String> classNames;
/**
* Construct a LitePalDB instance from the default configuration by litepal.xml. But database
* name must be different than the default.
* @param dbName
* Name of database.
* @return A LitePalDB instance which used the default configuration in litepal.xml but with a specified database name.
*/
public static LitePalDB fromDefault(String dbName) {
LitePalConfig config = LitePalParser.parseLitePalConfiguration();
LitePalDB litePalDB = new LitePalDB(dbName, config.getVersion());
litePalDB.setExternalStorage("external".equals(config.getStorage()));
litePalDB.setClassNames(config.getClassNames());
return litePalDB;
}
/**
* Construct a LitePalDB instance. Database name and version are necessary fields.
* @param dbName
* Name of database.
* @param version
* Version of database.
*/
public LitePalDB(String dbName, int version) {
this.dbName = dbName;
this.version = version;
}
public int getVersion() {
return version;
}
public String getDbName() {
return dbName;
}
public boolean isExternalStorage() {
return isExternalStorage;
}
public void setExternalStorage(boolean isExternalStorage) {
this.isExternalStorage = isExternalStorage;
}
/**
* Get the class name list. Always add table_schema as a value.
*
* @return The class name list.
*/
public List<String> getClassNames() {
if (classNames == null) {
classNames = new ArrayList<String>();
classNames.add("org.litepal.model.Table_Schema");
} else if (classNames.isEmpty()) {
classNames.add("org.litepal.model.Table_Schema");
}
return classNames;
}
/**
* Add a class name into the current mapping model list.
*
* @param className
* Full package class name.
*/
public void addClassName(String className) {
getClassNames().add(className);
}
void setClassNames(List<String> className) {
this.classNames = className;
}
}

@ -0,0 +1,54 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Used for adding constraints to a column. Note that this annotation won't affect id column.
*
* @author Tony Green
* @since 1.3
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
/**
* Set nullable constraint for the column.
*/
boolean nullable() default true;
/**
* Set unique constraint for the column.
*/
boolean unique() default false;
/**
* Set default value with String type for the column regardless of what column type is.
*/
String defaultValue() default "";
/**
* Ignore to map this field into a column.
*/
boolean ignore() default false;
}

@ -0,0 +1,193 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import org.litepal.LitePalBase;
import org.litepal.crud.model.AssociationsInfo;
import org.litepal.exceptions.DataSupportException;
import org.litepal.util.DBUtility;
/**
* Base class of associations analyzer.
*
* @author Tony Green
* @since 1.1
*/
abstract class AssociationsAnalyzer extends DataHandler {
/**
* Get the associated models collection of associated model. Used for
* reverse searching associations.
*
* @param associatedModel
* The associated model of baseObj.
* @param associationInfo
* To get reverse associated models collection.
* @return The associated models collection of associated model by analyzing
* associationInfo.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws java.lang.reflect.InvocationTargetException
*/
@SuppressWarnings("unchecked")
protected Collection<DataSupport> getReverseAssociatedModels(DataSupport associatedModel,
AssociationsInfo associationInfo) throws SecurityException, IllegalArgumentException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return (Collection<DataSupport>) getFieldValue(associatedModel,
associationInfo.getAssociateSelfFromOtherModel());
}
/**
* Set the associated models collection of associated model. Break quote of
* source collection.
*
* @param associatedModel
* The associated model of baseObj.
* @param associationInfo
* To get reverse associated models collection.
* @param associatedModelCollection
* The new associated models collection with same data as source
* collection but different quote.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws java.lang.reflect.InvocationTargetException
*/
protected void setReverseAssociatedModels(DataSupport associatedModel,
AssociationsInfo associationInfo, Collection<DataSupport> associatedModelCollection)
throws SecurityException, IllegalArgumentException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
setFieldValue(associatedModel, associationInfo.getAssociateSelfFromOtherModel(),
associatedModelCollection);
}
/**
* Check the associated model collection. If the associated model collection
* is null, try to initialize the associated model collection by the given
* associated field. If the associated field is subclass of List, make an
* instance of ArrayList for associated model collection. If the associated
* field is subclass of Set, make an instance of HashSet for associated
* model collection. If the associated model collection is not null, doing
* nothing.
*
* @param associatedModelCollection
* The associated model collection to check null and initialize.
* @param associatedField
* The field to decide which type to initialize for associated
* model collection.
* @throws DataSupportException
*/
protected Collection<DataSupport> checkAssociatedModelCollection(
Collection<DataSupport> associatedModelCollection, Field associatedField) {
Collection<DataSupport> collection = null;
if (isList(associatedField.getType())) {
collection = new ArrayList<DataSupport>();
} else if (isSet(associatedField.getType())) {
collection = new HashSet<DataSupport>();
} else {
throw new DataSupportException(DataSupportException.WRONG_FIELD_TYPE_FOR_ASSOCIATIONS);
}
if (associatedModelCollection != null) {
collection.addAll(associatedModelCollection);
}
return collection;
}
/**
* Build the bidirectional association by setting the baseObj instance to
* the associated model.
*
* @param baseObj
* The instance of self model.
* @param associatedModel
* The associated model.
* @param associationInfo
* The association info to get the association.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws java.lang.reflect.InvocationTargetException
*/
protected void buildBidirectionalAssociations(DataSupport baseObj, DataSupport associatedModel,
AssociationsInfo associationInfo) throws SecurityException, IllegalArgumentException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException {
setFieldValue(associatedModel, associationInfo.getAssociateSelfFromOtherModel(),
baseObj);
}
/**
* If the associated model is saved, add its' name and id to baseObj by
* calling {@link org.litepal.crud.DataSupport#addAssociatedModelWithFK(String, long)}. Or if
* the baseObj is saved, add its' name and id to associated model by calling
* {@link org.litepal.crud.DataSupport#addAssociatedModelWithoutFK(String, long)}.
*
* @param baseObj
* The baseObj currently want to persist.
* @param associatedModel
* The associated model.
*/
protected void dealsAssociationsOnTheSideWithoutFK(DataSupport baseObj,
DataSupport associatedModel) {
if (associatedModel != null) {
if (associatedModel.isSaved()) {
baseObj.addAssociatedModelWithFK(associatedModel.getTableName(),
associatedModel.getBaseObjId());
} else {
if (baseObj.isSaved()) {
associatedModel.addAssociatedModelWithoutFK(baseObj.getTableName(),
baseObj.getBaseObjId());
}
}
}
}
/**
* If the associated model of self model is null, the FK value in database
* should be cleared if it exists when updating.
*
* @param baseObj
* The baseObj currently want to persist or update.
* @param associationInfo
* The associated info analyzed by
* {@link LitePalBase#getAssociationInfo(String)}.
*/
protected void mightClearFKValue(DataSupport baseObj, AssociationsInfo associationInfo) {
baseObj.addFKNameToClearSelf(getForeignKeyName(associationInfo));
}
/**
* Get foreign key name by {@link org.litepal.crud.model.AssociationsInfo}.
*
* @param associationInfo
* To get foreign key name from.
* @return The foreign key name.
*/
private String getForeignKeyName(AssociationsInfo associationInfo) {
return getForeignKeyColumnName(DBUtility.getTableNameByClassName(associationInfo
.getAssociatedClassName()));
}
}

@ -0,0 +1,914 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud;
import java.util.List;
import org.litepal.LitePal;
import org.litepal.crud.async.AverageExecutor;
import org.litepal.crud.async.CountExecutor;
import org.litepal.crud.async.FindExecutor;
import org.litepal.crud.async.FindMultiExecutor;
import org.litepal.tablemanager.Connector;
import org.litepal.util.BaseUtility;
import org.litepal.util.DBUtility;
/**
* Allows developers to query tables with cluster style.
*
* @author Tony Green
* @since 1.1
*/
public class ClusterQuery {
/**
* Representing the selected columns in SQL.
*/
String[] mColumns;
/**
* Representing the where clause in SQL.
*/
String[] mConditions;
/**
* Representing the order by clause in SQL.
*/
String mOrderBy;
/**
* Representing the limit clause in SQL.
*/
String mLimit;
/**
* Representing the offset in SQL.
*/
String mOffset;
/**
* Do not allow to create instance by developers.
*/
ClusterQuery() {
}
/**
* Declaring to query which columns in table.
*
* <pre>
* DataSupport.select(&quot;name&quot;, &quot;age&quot;).find(Person.class);
* </pre>
*
* This will find all rows with name and age columns in Person table.
*
* @param columns
* A String array of which columns to return. Passing null will
* return all columns.
*
* @return A ClusterQuery instance.
*/
public ClusterQuery select(String... columns) {
mColumns = columns;
return this;
}
/**
* Declaring to query which rows in table.
*
* <pre>
* DataSupport.where(&quot;name = ? or age &gt; ?&quot;, &quot;Tom&quot;, &quot;14&quot;).find(Person.class);
* </pre>
*
* This will find rows which name is Tom or age greater than 14 in Person
* table.
*
* @param conditions
* A filter declaring which rows to return, formatted as an SQL
* WHERE clause. Passing null will return all rows.
* @return A ClusterQuery instance.
*/
public ClusterQuery where(String... conditions) {
mConditions = conditions;
return this;
}
/**
* Declaring how to order the rows queried from table.
*
* <pre>
* DataSupport.order(&quot;name desc&quot;).find(Person.class);
* </pre>
*
* This will find all rows in Person table sorted by name with inverted
* order.
*
* @param column
* How to order the rows, formatted as an SQL ORDER BY clause.
* Passing null will use the default sort order, which may be
* unordered.
* @return A ClusterQuery instance.
*/
public ClusterQuery order(String column) {
mOrderBy = column;
return this;
}
/**
* Limits the number of rows returned by the query.
*
* <pre>
* DataSupport.limit(2).find(Person.class);
* </pre>
*
* This will find the top 2 rows in Person table.
*
* @param value
* Limits the number of rows returned by the query, formatted as
* LIMIT clause.
* @return A ClusterQuery instance.
*/
public ClusterQuery limit(int value) {
mLimit = String.valueOf(value);
return this;
}
/**
* Declaring the offset of rows returned by the query. This method must be
* used with {@link #limit(int)}, or nothing will return.
*
* <pre>
* DataSupport.limit(1).offset(2).find(Person.class);
* </pre>
*
* This will find the third row in Person table.
*
* @param value
* The offset amount of rows returned by the query.
* @return A ClusterQuery instance.
*/
public ClusterQuery offset(int value) {
mOffset = String.valueOf(value);
return this;
}
/**
* Finds multiple records by the cluster parameters. You can use the below
* way to finish a complicated query:
*
* <pre>
* DataSupport.select(&quot;name&quot;).where(&quot;age &gt; ?&quot;, &quot;14&quot;).order(&quot;age&quot;).limit(1).offset(2)
* .find(Person.class);
* </pre>
*
* You can also do the same job with SQLiteDatabase like this:
*
* <pre>
* getSQLiteDatabase().query(&quot;Person&quot;, &quot;name&quot;, &quot;age &gt; ?&quot;, new String[] { &quot;14&quot; }, null, null, &quot;age&quot;,
* &quot;2,1&quot;);
* </pre>
*
* Obviously, the first way is much more semantic.<br>
* Note that the associated models won't be loaded by default considering
* the efficiency, but you can do that by using
* {@link org.litepal.crud.ClusterQuery#find(Class, boolean)}.
*
* @param modelClass
* Which table to query and the object type to return as a list.
* @return An object list with founded data from database, or an empty list.
*/
public <T> List<T> find(Class<T> modelClass) {
return find(modelClass, false);
}
/**
* Basically same as {@link #find(Class)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query and the object type to return as a list.
* @return A FindMultiExecutor instance.
*/
public <T> FindMultiExecutor findAsync(final Class<T> modelClass) {
return findAsync(modelClass, false);
}
/**
* It is mostly same as {@link org.litepal.crud.ClusterQuery#find(Class)} but an isEager
* parameter. If set true the associated models will be loaded as well.
* <br>
* Note that isEager will only work for one deep level relation, considering the query efficiency.
* You have to implement on your own if you need to load multiple deepness of relation at once.
*
* @param modelClass
* Which table to query and the object type to return as a list.
* @param isEager
* True to load the associated models, false not.
* @return An object list with founded data from database, or an empty list.
*/
public synchronized <T> List<T> find(Class<T> modelClass, boolean isEager) {
QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
String limit;
if (mOffset == null) {
limit = mLimit;
} else {
if (mLimit == null) {
mLimit = "0";
}
limit = mOffset + "," + mLimit;
}
return queryHandler.onFind(modelClass, mColumns, mConditions, mOrderBy, limit, isEager);
}
/**
* Basically same as {@link #find(Class, boolean)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query and the object type to return as a list.
* @param isEager
* True to load the associated models, false not.
* @return A FindMultiExecutor instance.
*/
public <T> FindMultiExecutor findAsync(final Class<T> modelClass, final boolean isEager) {
final FindMultiExecutor executor = new FindMultiExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (DataSupport.class) {
final List<T> t = find(modelClass, isEager);
if (executor.getListener() != null) {
LitePal.getHandler().post(new Runnable() {
@Override
public void run() {
executor.getListener().onFinish(t);
}
});
}
}
}
};
executor.submit(runnable);
return executor;
}
/**
* Finds the first record by the cluster parameters. You can use the below
* way to finish a complicated query:
*
* <pre>
* DataSupport.select(&quot;name&quot;).where(&quot;age &gt; ?&quot;, &quot;14&quot;).order(&quot;age&quot;).limit(1).offset(2)
* .findFirst(Person.class);
* </pre>
*
* Note that the associated models won't be loaded by default considering
* the efficiency, but you can do that by using
* {@link org.litepal.crud.ClusterQuery#findFirst(Class, boolean)}.
*
* @param modelClass
* Which table to query and the object type to return.
* @return An object with founded data from database, or null.
*/
public <T> T findFirst(Class<T> modelClass) {
return findFirst(modelClass, false);
}
/**
* Basically same as {@link #findFirst(Class)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query and the object type to return.
* @return A FindExecutor instance.
*/
public <T> FindExecutor findFirstAsync(Class<T> modelClass) {
return findFirstAsync(modelClass, false);
}
/**
* It is mostly same as {@link org.litepal.crud.ClusterQuery#findFirst(Class)} but an isEager
* parameter. If set true the associated models will be loaded as well.
* <br>
* Note that isEager will only work for one deep level relation, considering the query efficiency.
* You have to implement on your own if you need to load multiple deepness of relation at once.
*
* @param modelClass
* Which table to query and the object type to return.
* @param isEager
* True to load the associated models, false not.
* @return An object with founded data from database, or null.
*/
public <T> T findFirst(Class<T> modelClass, boolean isEager) {
List<T> list = find(modelClass, isEager);
if (list.size() > 0) {
return list.get(0);
}
return null;
}
/**
* Basically same as {@link #findFirst(Class, boolean)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query and the object type to return.
* @param isEager
* True to load the associated models, false not.
* @return A FindExecutor instance.
*/
public <T> FindExecutor findFirstAsync(final Class<T> modelClass, final boolean isEager) {
final FindExecutor executor = new FindExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (DataSupport.class) {
final T t = findFirst(modelClass, isEager);
if (executor.getListener() != null) {
LitePal.getHandler().post(new Runnable() {
@Override
public void run() {
executor.getListener().onFinish(t);
}
});
}
}
}
};
executor.submit(runnable);
return executor;
}
/**
* Finds the last record by the cluster parameters. You can use the below
* way to finish a complicated query:
*
* <pre>
* DataSupport.select(&quot;name&quot;).where(&quot;age &gt; ?&quot;, &quot;14&quot;).order(&quot;age&quot;).limit(1).offset(2)
* .findLast(Person.class);
* </pre>
*
* Note that the associated models won't be loaded by default considering
* the efficiency, but you can do that by using
* {@link org.litepal.crud.ClusterQuery#findLast(Class, boolean)}.
*
* @param modelClass
* Which table to query and the object type to return.
* @return An object with founded data from database, or null.
*/
public <T> T findLast(Class<T> modelClass) {
return findLast(modelClass, false);
}
/**
* Basically same as {@link #findLast(Class)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query and the object type to return.
* @return A FindExecutor instance.
*/
public <T> FindExecutor findLastAsync(Class<T> modelClass) {
return findLastAsync(modelClass, false);
}
/**
* It is mostly same as {@link org.litepal.crud.ClusterQuery#findLast(Class)} but an isEager
* parameter. If set true the associated models will be loaded as well.
* <br>
* Note that isEager will only work for one deep level relation, considering the query efficiency.
* You have to implement on your own if you need to load multiple deepness of relation at once.
*
* @param modelClass
* Which table to query and the object type to return.
* @param isEager
* True to load the associated models, false not.
* @return An object with founded data from database, or null.
*/
public <T> T findLast(Class<T> modelClass, boolean isEager) {
List<T> list = find(modelClass, isEager);
int size = list.size();
if (size > 0) {
return list.get(size - 1);
}
return null;
}
/**
* Basically same as {@link #findLast(Class, boolean)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query and the object type to return.
* @param isEager
* True to load the associated models, false not.
* @return A FindExecutor instance.
*/
public <T> FindExecutor findLastAsync(final Class<T> modelClass, final boolean isEager) {
final FindExecutor executor = new FindExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (DataSupport.class) {
final T t = findLast(modelClass, isEager);
if (executor.getListener() != null) {
LitePal.getHandler().post(new Runnable() {
@Override
public void run() {
executor.getListener().onFinish(t);
}
});
}
}
}
};
executor.submit(runnable);
return executor;
}
/**
* Count the records.
*
* <pre>
* DataSupport.count(Person.class);
* </pre>
*
* This will count all rows in person table.<br>
* You can also specify a where clause when counting.
*
* <pre>
* DataSupport.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(Person.class);
* </pre>
*
* @param modelClass
* Which table to query from by class.
* @return Count of the specified table.
*/
public synchronized int count(Class<?> modelClass) {
return count(BaseUtility.changeCase(modelClass.getSimpleName()));
}
/**
* Basically same as {@link #count(Class)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query from by class.
* @return A CountExecutor instance.
*/
public CountExecutor countAsync(Class<?> modelClass) {
return countAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())));
}
/**
* Count the records.
*
* <pre>
* DataSupport.count(&quot;person&quot;);
* </pre>
*
* This will count all rows in person table.<br>
* You can also specify a where clause when counting.
*
* <pre>
* DataSupport.where(&quot;age &gt; ?&quot;, &quot;15&quot;).count(&quot;person&quot;);
* </pre>
*
* @param tableName
* Which table to query from.
* @return Count of the specified table.
*/
public synchronized int count(String tableName) {
QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
return queryHandler.onCount(tableName, mConditions);
}
/**
* Basically same as {@link #count(String)} but pending to a new thread for executing.
*
* @param tableName
* Which table to query from.
* @return A CountExecutor instance.
*/
public CountExecutor countAsync(final String tableName) {
final CountExecutor executor = new CountExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (DataSupport.class) {
final int count = count(tableName);
if (executor.getListener() != null) {
LitePal.getHandler().post(new Runnable() {
@Override
public void run() {
executor.getListener().onFinish(count);
}
});
}
}
}
};
executor.submit(runnable);
return executor;
}
/**
* Calculates the average value on a given column.
*
* <pre>
* DataSupport.average(Person.class, &quot;age&quot;);
* </pre>
*
* You can also specify a where clause when calculating.
*
* <pre>
* DataSupport.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(Person.class, &quot;age&quot;);
* </pre>
*
* @param modelClass
* Which table to query from by class.
* @param column
* The based on column to calculate.
* @return The average value on a given column.
*/
public synchronized double average(Class<?> modelClass, String column) {
return average(BaseUtility.changeCase(modelClass.getSimpleName()), column);
}
/**
* Basically same as {@link #average(Class, String)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query from by class.
* @param column
* The based on column to calculate.
* @return A AverageExecutor instance.
*/
public AverageExecutor averageAsync(final Class<?> modelClass, final String column) {
return averageAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), column);
}
/**
* Calculates the average value on a given column.
*
* <pre>
* DataSupport.average(&quot;person&quot;, &quot;age&quot;);
* </pre>
*
* You can also specify a where clause when calculating.
*
* <pre>
* DataSupport.where(&quot;age &gt; ?&quot;, &quot;15&quot;).average(&quot;person&quot;, &quot;age&quot;);
* </pre>
*
* @param tableName
* Which table to query from.
* @param column
* The based on column to calculate.
* @return The average value on a given column.
*/
public synchronized double average(String tableName, String column) {
QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
return queryHandler.onAverage(tableName, column, mConditions);
}
/**
* Basically same as {@link #average(String, String)} but pending to a new thread for executing.
*
* @param tableName
* Which table to query from.
* @param column
* The based on column to calculate.
* @return A AverageExecutor instance.
*/
public AverageExecutor averageAsync(final String tableName, final String column) {
final AverageExecutor executor = new AverageExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (DataSupport.class) {
final double average = average(tableName, column);
if (executor.getListener() != null) {
LitePal.getHandler().post(new Runnable() {
@Override
public void run() {
executor.getListener().onFinish(average);
}
});
}
}
}
};
executor.submit(runnable);
return executor;
}
/**
* Calculates the maximum value on a given column. The value is returned
* with the same data type of the column.
*
* <pre>
* DataSupport.max(Person.class, &quot;age&quot;, int.class);
* </pre>
*
* You can also specify a where clause when calculating.
*
* <pre>
* DataSupport.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(Person.class, &quot;age&quot;, Integer.TYPE);
* </pre>
*
* @param modelClass
* Which table to query from by class.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return The maximum value on a given column.
*/
public synchronized <T> T max(Class<?> modelClass, String columnName, Class<T> columnType) {
return max(BaseUtility.changeCase(modelClass.getSimpleName()), columnName, columnType);
}
/**
* Basically same as {@link #max(Class, String, Class)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query from by class.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return A FindExecutor instance.
*/
public <T> FindExecutor maxAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {
return maxAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);
}
/**
* Calculates the maximum value on a given column. The value is returned
* with the same data type of the column.
*
* <pre>
* DataSupport.max(&quot;person&quot;, &quot;age&quot;, int.class);
* </pre>
*
* You can also specify a where clause when calculating.
*
* <pre>
* DataSupport.where(&quot;age &gt; ?&quot;, &quot;15&quot;).max(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);
* </pre>
*
* @param tableName
* Which table to query from.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return The maximum value on a given column.
*/
public synchronized <T> T max(String tableName, String columnName, Class<T> columnType) {
QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
return queryHandler.onMax(tableName, columnName, mConditions, columnType);
}
/**
* Basically same as {@link #max(String, String, Class)} but pending to a new thread for executing.
*
* @param tableName
* Which table to query from.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return A FindExecutor instance.
*/
public <T> FindExecutor maxAsync(final String tableName, final String columnName, final Class<T> columnType) {
final FindExecutor executor = new FindExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (DataSupport.class) {
final T t = max(tableName, columnName, columnType);
if (executor.getListener() != null) {
LitePal.getHandler().post(new Runnable() {
@Override
public void run() {
executor.getListener().onFinish(t);
}
});
}
}
}
};
executor.submit(runnable);
return executor;
}
/**
* Calculates the minimum value on a given column. The value is returned
* with the same data type of the column.
*
* <pre>
* DataSupport.min(Person.class, &quot;age&quot;, int.class);
* </pre>
*
* You can also specify a where clause when calculating.
*
* <pre>
* DataSupport.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(Person.class, &quot;age&quot;, Integer.TYPE);
* </pre>
*
* @param modelClass
* Which table to query from by class.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return The minimum value on a given column.
*/
public synchronized <T> T min(Class<?> modelClass, String columnName, Class<T> columnType) {
return min(BaseUtility.changeCase(modelClass.getSimpleName()), columnName, columnType);
}
/**
* Basically same as {@link #min(Class, String, Class)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query from by class.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return A FindExecutor instance.
*/
public <T> FindExecutor minAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {
return minAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);
}
/**
* Calculates the minimum value on a given column. The value is returned
* with the same data type of the column.
*
* <pre>
* DataSupport.min(&quot;person&quot;, &quot;age&quot;, int.class);
* </pre>
*
* You can also specify a where clause when calculating.
*
* <pre>
* DataSupport.where(&quot;age &gt; ?&quot;, &quot;15&quot;).min(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);
* </pre>
*
* @param tableName
* Which table to query from.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return The minimum value on a given column.
*/
public synchronized <T> T min(String tableName, String columnName, Class<T> columnType) {
QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
return queryHandler.onMin(tableName, columnName, mConditions, columnType);
}
/**
* Basically same as {@link #min(String, String, Class)} but pending to a new thread for executing.
*
* @param tableName
* Which table to query from.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return A FindExecutor instance.
*/
public <T> FindExecutor minAsync(final String tableName, final String columnName, final Class<T> columnType) {
final FindExecutor executor = new FindExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (DataSupport.class) {
final T t = min(tableName, columnName, columnType);
if (executor.getListener() != null) {
LitePal.getHandler().post(new Runnable() {
@Override
public void run() {
executor.getListener().onFinish(t);
}
});
}
}
}
};
executor.submit(runnable);
return executor;
}
/**
* Calculates the sum of values on a given column. The value is returned
* with the same data type of the column.
*
* <pre>
* DataSupport.sum(Person.class, &quot;age&quot;, int.class);
* </pre>
*
* You can also specify a where clause when calculating.
*
* <pre>
* DataSupport.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(Person.class, &quot;age&quot;, Integer.TYPE);
* </pre>
*
* @param modelClass
* Which table to query from by class.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return The sum value on a given column.
*/
public synchronized <T> T sum(Class<?> modelClass, String columnName, Class<T> columnType) {
return sum(BaseUtility.changeCase(modelClass.getSimpleName()), columnName, columnType);
}
/**
* Basically same as {@link #sum(Class, String, Class)} but pending to a new thread for executing.
*
* @param modelClass
* Which table to query from by class.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return A FindExecutor instance.
*/
public <T> FindExecutor sumAsync(final Class<?> modelClass, final String columnName, final Class<T> columnType) {
return sumAsync(BaseUtility.changeCase(DBUtility.getTableNameByClassName(modelClass.getName())), columnName, columnType);
}
/**
* Calculates the sum of values on a given column. The value is returned
* with the same data type of the column.
*
* <pre>
* DataSupport.sum(&quot;person&quot;, &quot;age&quot;, int.class);
* </pre>
*
* You can also specify a where clause when calculating.
*
* <pre>
* DataSupport.where(&quot;age &gt; ?&quot;, &quot;15&quot;).sum(&quot;person&quot;, &quot;age&quot;, Integer.TYPE);
* </pre>
*
* @param tableName
* Which table to query from.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return The sum value on a given column.
*/
public synchronized <T> T sum(String tableName, String columnName, Class<T> columnType) {
QueryHandler queryHandler = new QueryHandler(Connector.getDatabase());
return queryHandler.onSum(tableName, columnName, mConditions, columnType);
}
/**
* Basically same as {@link #sum(String, String, Class)} but pending to a new thread for executing.
*
* @param tableName
* Which table to query from.
* @param columnName
* The based on column to calculate.
* @param columnType
* The type of the based on column.
* @return A FindExecutor instance.
*/
public <T> FindExecutor sumAsync(final String tableName, final String columnName, final Class<T> columnType) {
final FindExecutor executor = new FindExecutor();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (DataSupport.class) {
final T t = sum(tableName, columnName, columnType);
if (executor.getListener() != null) {
LitePal.getHandler().post(new Runnable() {
@Override
public void run() {
executor.getListener().onFinish(t);
}
});
}
}
}
};
executor.submit(runnable);
return executor;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,398 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.litepal.crud.model.AssociationsInfo;
import org.litepal.exceptions.DataSupportException;
import org.litepal.util.BaseUtility;
import org.litepal.util.Const;
import org.litepal.util.DBUtility;
import android.database.sqlite.SQLiteDatabase;
/**
* This is a component under DataSupport. It deals with the deleting stuff as
* primary task. If deletes a saved model or delete a record with id, the
* cascade delete function would work. But considering efficiency, if deletes
* with deleteAll method, the referenced data in other tables will not be
* affected. Developers should remove those referenced data by their own.
*
* @author Tony Green
* @since 1.1
*/
class DeleteHandler extends DataHandler {
/**
* To store associated tables of current model's table. Only used while
* deleting by id and deleting all by model class.
*/
private List<String> foreignKeyTableToDelete;
/**
* Initialize {@link org.litepal.crud.DataHandler#mDatabase} for operating database. Do not
* allow to create instance of DeleteHandler out of CRUD package.
*
* @param db
* The instance of SQLiteDatabase.
*/
DeleteHandler(SQLiteDatabase db) {
mDatabase = db;
}
/**
* The open interface for other classes in CRUD package to delete. Using
* baseObj to decide which record to delete. The baseObj must be saved
* already(using {@link org.litepal.crud.DataSupport#isSaved()} to test), or nothing will be
* deleted. This method can action cascade delete. When the record is
* deleted from database, all the referenced data such as foreign key value
* will be removed too.
*
* @param baseObj
* The record to delete.
* @return The number of rows affected. Including cascade delete rows.
*/
int onDelete(DataSupport baseObj) {
if (baseObj.isSaved()) {
List<Field> supportedGenericFields = getSupportedGenericFields(baseObj.getClassName());
deleteGenericData(baseObj.getClass(), supportedGenericFields, baseObj.getBaseObjId());
Collection<AssociationsInfo> associationInfos = analyzeAssociations(baseObj);
int rowsAffected = deleteCascade(baseObj);
rowsAffected += mDatabase.delete(baseObj.getTableName(), "id = "
+ baseObj.getBaseObjId(), null);
clearAssociatedModelSaveState(baseObj, associationInfos);
return rowsAffected;
}
return 0;
}
/**
* The open interface for other classes in CRUD package to delete. Using
* modelClass to decide which table to delete from, and id to decide a
* specific row. This method can action cascade delete. When the record is
* deleted from database, all the referenced data such as foreign key value
* will be removed too.
*
* @param modelClass
* Which table to delete from.
* @param id
* Which record to delete.
* @return The number of rows affected. Including cascade delete rows.
*/
int onDelete(Class<?> modelClass, long id) {
List<Field> supportedGenericFields = getSupportedGenericFields(modelClass.getName());
deleteGenericData(modelClass, supportedGenericFields, id);
analyzeAssociations(modelClass);
int rowsAffected = deleteCascade(modelClass, id);
rowsAffected += mDatabase.delete(getTableName(modelClass),
"id = " + id, null);
getForeignKeyTableToDelete().clear();
return rowsAffected;
}
/**
* The open interface for other classes in CRUD package to delete multiple
* rows. Using tableName to decide which table to delete from, and
* conditions representing the WHERE part of an SQL statement.
*
* @param tableName
* Which table to delete from.
* @param conditions
* A string array representing the WHERE part of an SQL
* statement.
* @return The number of rows affected.
*/
int onDeleteAll(String tableName, String... conditions) {
BaseUtility.checkConditionsCorrect(conditions);
if (conditions != null && conditions.length > 0) {
conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);
}
return mDatabase.delete(tableName, getWhereClause(conditions),
getWhereArgs(conditions));
}
@SuppressWarnings("unchecked")
int onDeleteAll(Class<?> modelClass, String... conditions) {
BaseUtility.checkConditionsCorrect(conditions);
if (conditions != null && conditions.length > 0) {
conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);
}
List<Field> supportedGenericFields = getSupportedGenericFields(modelClass.getName());
if (!supportedGenericFields.isEmpty()) {
List<DataSupport> list = (List<DataSupport>) DataSupport.select("id").where(conditions).find(modelClass);
if (list.size() > 0) {
long[] ids = new long[list.size()];
for (int i = 0; i < ids.length; i++) {
DataSupport dataSupport = list.get(i);
ids[i] = dataSupport.getBaseObjId();
}
deleteGenericData(modelClass, supportedGenericFields, ids);
}
}
analyzeAssociations(modelClass);
int rowsAffected = deleteAllCascade(modelClass, conditions);
rowsAffected += mDatabase.delete(getTableName(modelClass), getWhereClause(conditions),
getWhereArgs(conditions));
getForeignKeyTableToDelete().clear();
return rowsAffected;
}
/**
* Analyze the associations of modelClass and store the associated tables.
* The associated tables might be used when deleting referenced data of a
* specified row.
*
* @param modelClass
* To get associations of this class.
*/
private void analyzeAssociations(Class<?> modelClass) {
Collection<AssociationsInfo> associationInfos = getAssociationInfo(modelClass
.getName());
for (AssociationsInfo associationInfo : associationInfos) {
String associatedTableName = DBUtility
.getTableNameByClassName(associationInfo
.getAssociatedClassName());
if (associationInfo.getAssociationType() == Const.Model.MANY_TO_ONE
|| associationInfo.getAssociationType() == Const.Model.ONE_TO_ONE) {
String classHoldsForeignKey = associationInfo
.getClassHoldsForeignKey();
if (!modelClass.getName().equals(classHoldsForeignKey)) {
getForeignKeyTableToDelete().add(associatedTableName);
}
} else if (associationInfo.getAssociationType() == Const.Model.MANY_TO_MANY) {
String joinTableName = DBUtility.getIntermediateTableName(
getTableName(modelClass), associatedTableName);
joinTableName = BaseUtility.changeCase(joinTableName);
getForeignKeyTableToDelete().add(joinTableName);
}
}
}
/**
* Use the analyzed result of associations to delete referenced data. So
* this method must be called after {@link #analyzeAssociations(Class)}.
* There're two parts of referenced data to delete. The foreign key rows in
* associated table and the foreign key rows in intermediate join table.
*
* @param modelClass
* To get the table name and combine with id as a foreign key
* column.
* @param id
* Delete all the rows which referenced with this id.
* @return The number of rows affected in associated tables and intermediate
* join tables.
*/
private int deleteCascade(Class<?> modelClass, long id) {
int rowsAffected = 0;
for (String associatedTableName : getForeignKeyTableToDelete()) {
String fkName = getForeignKeyColumnName(getTableName(modelClass));
rowsAffected += mDatabase.delete(associatedTableName, fkName
+ " = " + id, null);
}
return rowsAffected;
}
private int deleteAllCascade(Class<?> modelClass, String... conditions) {
int rowsAffected = 0;
for (String associatedTableName : getForeignKeyTableToDelete()) {
String tableName = getTableName(modelClass);
String fkName = getForeignKeyColumnName(tableName);
StringBuilder whereClause = new StringBuilder();
whereClause.append(fkName).append(" in (select id from ");
whereClause.append(tableName);
if (conditions != null && conditions.length > 0) {
whereClause.append(" where ").append(buildConditionString(conditions));
}
whereClause.append(")");
rowsAffected += mDatabase.delete(associatedTableName,
BaseUtility.changeCase(whereClause.toString()), null);
}
return rowsAffected;
}
private String buildConditionString(String... conditions) {
int argCount = conditions.length - 1;
String whereClause = conditions[0];
for (int i = 0; i < argCount; i++) {
whereClause = whereClause.replaceFirst("\\?", "'" + conditions[i+1] + "'");
}
return whereClause;
}
/**
* Analyze the associations of baseObj and store the result in it. The
* associations will be used when deleting referenced data of baseObj.
*
* @param baseObj
* The record to delete.
*/
private Collection<AssociationsInfo> analyzeAssociations(DataSupport baseObj) {
try {
Collection<AssociationsInfo> associationInfos = getAssociationInfo(baseObj
.getClassName());
analyzeAssociatedModels(baseObj, associationInfos);
return associationInfos;
} catch (Exception e) {
throw new DataSupportException(e.getMessage(), e);
}
}
/**
* Clear associated models' save state. After this method, the associated
* models of baseObj which data is removed from database will become
* unsaved.
*
* @param baseObj
* The record to delete.
* @param associationInfos
* The associated info.
*/
private void clearAssociatedModelSaveState(DataSupport baseObj,
Collection<AssociationsInfo> associationInfos) {
try {
for (AssociationsInfo associationInfo : associationInfos) {
if (associationInfo.getAssociationType() == Const.Model.MANY_TO_ONE
&& !baseObj.getClassName().equals(
associationInfo.getClassHoldsForeignKey())) {
Collection<DataSupport> associatedModels = getAssociatedModels(
baseObj, associationInfo);
if (associatedModels != null && !associatedModels.isEmpty()) {
for (DataSupport model : associatedModels) {
if (model != null) {
model.clearSavedState();
}
}
}
} else if (associationInfo.getAssociationType() == Const.Model.ONE_TO_ONE) {
DataSupport model = getAssociatedModel(baseObj,
associationInfo);
if (model != null) {
model.clearSavedState();
}
}
}
} catch (Exception e) {
throw new DataSupportException(e.getMessage(), e);
}
}
/**
* Use the analyzed result of associations to delete referenced data. So
* this method must be called after
* {@link #analyzeAssociations(org.litepal.crud.DataSupport)}. There're two parts of
* referenced data to delete. The foreign key rows in associated tables and
* the foreign key rows in intermediate join tables.
*
* @param baseObj
* The record to delete. Now contains associations info.
* @return The number of rows affected in associated table and intermediate
* join table.
*/
private int deleteCascade(DataSupport baseObj) {
int rowsAffected;
rowsAffected = deleteAssociatedForeignKeyRows(baseObj);
rowsAffected += deleteAssociatedJoinTableRows(baseObj);
return rowsAffected;
}
/**
* Delete the referenced rows of baseObj in associated tables(Many2One and
* One2One conditions).
*
* @param baseObj
* The record to delete. Now contains associations info.
* @return The number of rows affected in all associated tables.
*/
private int deleteAssociatedForeignKeyRows(DataSupport baseObj) {
int rowsAffected = 0;
Map<String, Set<Long>> associatedModelMap = baseObj
.getAssociatedModelsMapWithFK();
for (String associatedTableName : associatedModelMap.keySet()) {
String fkName = getForeignKeyColumnName(baseObj.getTableName());
rowsAffected += mDatabase.delete(associatedTableName, fkName
+ " = " + baseObj.getBaseObjId(), null);
}
return rowsAffected;
}
/**
* Delete the referenced rows of baseObj in intermediate join
* tables(Many2Many condition).
*
* @param baseObj
* The record to delete. Now contains associations info.
* @return The number of rows affected in all intermediate join tables.
*/
private int deleteAssociatedJoinTableRows(DataSupport baseObj) {
int rowsAffected = 0;
Set<String> associatedTableNames = baseObj
.getAssociatedModelsMapForJoinTable().keySet();
for (String associatedTableName : associatedTableNames) {
String joinTableName = DBUtility.getIntermediateTableName(
baseObj.getTableName(), associatedTableName);
String fkName = getForeignKeyColumnName(baseObj.getTableName());
rowsAffected += mDatabase.delete(joinTableName, fkName + " = "
+ baseObj.getBaseObjId(), null);
}
return rowsAffected;
}
/**
* Get all the associated tables of current model's table. Only used while
* deleting by id.
*
* @return All the associated tables of current model's table.
*/
private List<String> getForeignKeyTableToDelete() {
if (foreignKeyTableToDelete == null) {
foreignKeyTableToDelete = new ArrayList<String>();
}
return foreignKeyTableToDelete;
}
/**
* Delete the generic data in generic tables while main data was deleted.
* @param modelClass
* Used to get the generic table name and value id column.
* @param supportedGenericFields
* List of all supported generic fields.
* @param ids
* The id array of models.
*/
private void deleteGenericData(Class<?> modelClass, List<Field> supportedGenericFields, long... ids) {
for (Field field : supportedGenericFields) {
String tableName = DBUtility.getGenericTableName(modelClass.getName(), field.getName());
String genericValueIdColumnName = DBUtility.getGenericValueIdColumnName(modelClass.getName());
StringBuilder whereClause = new StringBuilder();
boolean needOr = false;
for (long id : ids) {
if (needOr) {
whereClause.append(" or ");
}
whereClause.append(genericValueIdColumnName).append(" = ").append(id);
needOr = true;
}
mDatabase.delete(tableName, whereClause.toString(), null);
}
}
}

@ -0,0 +1,147 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.litepal.exceptions.DataSupportException;
/**
* This provides a send method to allow calling method in dynamic way. (Just
* like Ruby, but not clean or easy as Ruby to use).
*
* @author Tony Green
* @since 1.1
*/
class DynamicExecutor {
/**
* Disable to create an instance of DynamicExecutor.
*/
private DynamicExecutor() {
}
/**
* This method use java reflect API to execute method dynamically. Most
* importantly, it could access the methods with private modifier to break
* encapsulation.
*
* @param object
* The object to invoke method.
* @param methodName
* The method name to invoke.
* @param parameters
* The parameters.
* @param objectClass
* Use objectClass to find method to invoke.
* @param parameterTypes
* The parameter types.
* @return Returns the result of dynamically invoking method.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws java.lang.reflect.InvocationTargetException
*/
static Object send(Object object, String methodName, Object[] parameters, Class<?> objectClass,
Class<?>[] parameterTypes) throws SecurityException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
try {
if (parameters == null) {
parameters = new Object[] {};
}
if (parameterTypes == null) {
parameterTypes = new Class[] {};
}
Method method = objectClass.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
return method.invoke(object, parameters);
} catch (NoSuchMethodException e) {
throw new DataSupportException(DataSupportException.noSuchMethodException(
objectClass.getSimpleName(), methodName), e);
}
}
static void set(Object object, String fieldName, Object value, Class<?> objectClass)
throws SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchFieldException {
Field objectField = objectClass.getDeclaredField(fieldName);
objectField.setAccessible(true);
objectField.set(object, value);
}
/**
* This method use java reflect API to set field value dynamically. Most
* importantly, it could access fields with private modifier to break
* encapsulation.
*
* @param object
* The object to access.
* @param fieldName
* The field name to access.
* @param value
* Assign this value to field.
* @param objectClass
* The class of object.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
static void setField(Object object, String fieldName, Object value, Class<?> objectClass)
throws SecurityException, IllegalArgumentException, IllegalAccessException {
if (objectClass == DataSupport.class || objectClass == Object.class) {
throw new DataSupportException(DataSupportException.noSuchFieldExceptioin(
objectClass.getSimpleName(), fieldName));
}
try {
set(object, fieldName, value, objectClass);
} catch (NoSuchFieldException e) {
setField(object, fieldName, value, objectClass.getSuperclass());
}
}
/**
* This method use java reflect API to get field value dynamically. Most
* importantly, it could access fields with private modifier to break
* encapsulation.
*
* @param object
* The object to access.
* @param fieldName
* The field name to access.
* @param objectClass
* The class of object.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
static Object getField(Object object, String fieldName, Class<?> objectClass)
throws IllegalArgumentException, IllegalAccessException {
if (objectClass == DataSupport.class || objectClass == Object.class) {
throw new DataSupportException(DataSupportException.noSuchFieldExceptioin(
objectClass.getSimpleName(), fieldName));
}
try {
Field objectField = objectClass.getDeclaredField(fieldName);
objectField.setAccessible(true);
return objectField.get(object);
} catch (NoSuchFieldException e) {
return getField(object, fieldName, objectClass.getSuperclass());
}
}
}

@ -0,0 +1,220 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import org.litepal.LitePalBase;
import org.litepal.crud.model.AssociationsInfo;
import org.litepal.tablemanager.Connector;
import org.litepal.util.BaseUtility;
import org.litepal.util.DBUtility;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
/**
* Deals analysis work when comes to two models are associated with Many2Many
* associations.
*
* @author Tony Green
* @since 1.1
*/
public class Many2ManyAnalyzer extends AssociationsAnalyzer {
/**
* Analyzing the AssociationInfo. It will help baseObj assign the necessary
* values automatically. If the two associated models have bidirectional
* associations in class files but developer has only build unidirectional
* associations in models, it will force to build the bidirectional
* associations. Besides the
* {@link org.litepal.crud.DataSupport#addAssociatedModelForJoinTable(String, long)} will be called
* here to put right values into tables.
*
* @param baseObj
* The baseObj currently want to persist or update.
* @param associationInfo
* The associated info analyzed by
* {@link LitePalBase#getAssociationInfo(String)}.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws java.lang.reflect.InvocationTargetException
*/
void analyze(DataSupport baseObj, AssociationsInfo associationInfo) throws SecurityException,
IllegalArgumentException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
Collection<DataSupport> associatedModels = getAssociatedModels(baseObj, associationInfo);
declareAssociations(baseObj, associationInfo);
if (associatedModels != null) {
for (DataSupport associatedModel : associatedModels) {
Collection<DataSupport> tempCollection = getReverseAssociatedModels(
associatedModel, associationInfo);
Collection<DataSupport> reverseAssociatedModels = checkAssociatedModelCollection(
tempCollection, associationInfo.getAssociateSelfFromOtherModel());
addNewModelForAssociatedModel(reverseAssociatedModels, baseObj);
setReverseAssociatedModels(associatedModel, associationInfo,
reverseAssociatedModels);
dealAssociatedModel(baseObj, associatedModel);
}
}
}
/**
* This add an empty set for {@link org.litepal.crud.DataSupport#associatedModelsMapForJoinTable}.
* Might use for updating intermediate join table.
*
* @param baseObj
* The baseObj currently want to persist or update.
* @param associationInfo
* To get associated table name from.
*/
private void declareAssociations(DataSupport baseObj, AssociationsInfo associationInfo) {
baseObj.addEmptyModelForJoinTable(getAssociatedTableName(associationInfo));
}
/**
* Force to build bidirectional associations for the associated model. If it
* has already built, ignoring the rest process.
*
* @param associatedModelCollection
* The associated models collection of the associated model. Add
* self model into it if it doesn't contain self model yet.
* @param baseObj
* The baseObj currently want to persist or update.
*/
private void addNewModelForAssociatedModel(Collection<DataSupport> associatedModelCollection,
DataSupport baseObj) {
if (!associatedModelCollection.contains(baseObj)) {
associatedModelCollection.add(baseObj);
}
}
/**
* First of all the associated model need to be saved already, or nothing
* will be executed below. Then add the id of associated model into
* {@link org.litepal.crud.DataSupport#associatedModelsMapForJoinTable} for
* inserting value into intermediate join table after baseObj is saved.
*
* @param baseObj
* The baseObj currently want to persist or update.
* @param associatedModel
* The associated model of baseObj.
*/
private void dealAssociatedModel(DataSupport baseObj, DataSupport associatedModel) {
if (associatedModel.isSaved()) {
baseObj.addAssociatedModelForJoinTable(associatedModel.getTableName(),
associatedModel.getBaseObjId());
}
}
/**
* Get the associated table name by {@link org.litepal.crud.model.AssociationsInfo} after case
* changed.
*
* @param associationInfo
* To get the associated table name from.
* @return The name of associated table with changed case.
*/
private String getAssociatedTableName(AssociationsInfo associationInfo) {
return BaseUtility.changeCase(DBUtility.getTableNameByClassName(associationInfo
.getAssociatedClassName()));
}
/**
* Check if the associations between self model and associated model is
* already saved into intermediate join table.<br>
* Make sure baseObj and associatedModel are saved already, or the result
* might be wrong.
*
* @param baseObj
* The baseObj currently want to persist or update.
* @param associatedModel
* The associated model of baseObj.
* @return If the associations between them is saved into intermediate join
* table, return true. Otherwise return false.
*/
@SuppressWarnings("unused")
@Deprecated
private boolean isDataExists(DataSupport baseObj, DataSupport associatedModel) {
boolean exists = false;
SQLiteDatabase db = Connector.getDatabase();
Cursor cursor = null;
try {
cursor = db.query(getJoinTableName(baseObj, associatedModel), null,
getSelection(baseObj, associatedModel),
getSelectionArgs(baseObj, associatedModel), null, null, null);
exists = cursor.getCount() > 0;
} catch (Exception e) {
e.printStackTrace();
return true;
} finally {
cursor.close();
}
return exists;
}
/**
* Build the selection for querying the data in table. Column names are the
* table names with _id as suffix.
*
* @param baseObj
* The baseObj currently want to persist or update.
* @param associatedModel
* The associated model of baseObj.
* @return The selection clause for querying data.
*/
private String getSelection(DataSupport baseObj, DataSupport associatedModel) {
StringBuilder where = new StringBuilder();
where.append(getForeignKeyColumnName(baseObj.getTableName()));
where.append(" = ? and ");
where.append(getForeignKeyColumnName(associatedModel.getTableName()));
where.append(" = ?");
return where.toString();
}
/**
* Build the selection arguments to fill selection clause.
*
* @param baseObj
* The baseObj currently want to persist or update.
* @param associatedModel
* The associated model of baseObj.
* @return The selection arguments with the id of baseObj and
* associatedModel to fill.
*/
private String[] getSelectionArgs(DataSupport baseObj, DataSupport associatedModel) {
return new String[] { String.valueOf(baseObj.getBaseObjId()),
String.valueOf(associatedModel.getBaseObjId()) };
}
/**
* Get the intermediate join table name for self model and associated model.
*
* @param baseObj
* The baseObj currently want to persist or update.
* @param associatedModel
* The associated model of baseObj.
* @return The intermediate join table name.
*/
private String getJoinTableName(DataSupport baseObj, DataSupport associatedModel) {
return getIntermediateTableName(baseObj, associatedModel.getTableName());
}
}

@ -0,0 +1,175 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import org.litepal.LitePalBase;
import org.litepal.crud.model.AssociationsInfo;
import org.litepal.util.DBUtility;
/**
* Deals analysis work when comes to two models are associated with Many2One
* associations.
*
* @author Tony Green
* @since 1.1
*/
class Many2OneAnalyzer extends AssociationsAnalyzer {
/**
* Analyzing the AssociationInfo. It will help baseObj assign the necessary
* values automatically. If the two associated models have bidirectional
* associations in class files but developer has only build unidirectional
* associations in models, it will force to build the bidirectional
* associations. Besides
* {@link org.litepal.crud.DataSupport#addAssociatedModelWithFK(String, long)} and
* {@link org.litepal.crud.DataSupport#addAssociatedModelWithoutFK(String, long)} will be
* called here to put right values into tables.
*
* @param baseObj
* The baseObj currently want to persist.
* @param associationInfo
* The associated info analyzed by
* {@link LitePalBase#getAssociationInfo(String)}.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws java.lang.reflect.InvocationTargetException
*/
void analyze(DataSupport baseObj, AssociationsInfo associationInfo) throws SecurityException,
IllegalArgumentException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
if (baseObj.getClassName().equals(associationInfo.getClassHoldsForeignKey())) {
analyzeManySide(baseObj, associationInfo);
} else {
analyzeOneSide(baseObj, associationInfo);
}
}
/**
* When it's on the M side. Get the associated model first, then use it to
* get the associated model collection on the O side. Initialize the
* collection by calling
* {@link #checkAssociatedModelCollection(java.util.Collection, java.lang.reflect.Field)}
* and calling
* {@link #dealAssociatedModelOnManySide(java.util.Collection, org.litepal.crud.DataSupport, org.litepal.crud.DataSupport)}
* to set foreign key.
*
* @param baseObj
* The baseObj currently want to persist or update.
* @param associationInfo
* The associated info analyzed by
* {@link LitePalBase#getAssociationInfo(String)}.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws java.lang.reflect.InvocationTargetException
*/
private void analyzeManySide(DataSupport baseObj, AssociationsInfo associationInfo)
throws SecurityException, IllegalArgumentException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
DataSupport associatedModel = getAssociatedModel(baseObj, associationInfo);
if (associatedModel != null) {
// now it's m2o bidirectional association.
Collection<DataSupport> tempCollection = getReverseAssociatedModels(associatedModel,
associationInfo);
Collection<DataSupport> reverseAssociatedModels = checkAssociatedModelCollection(
tempCollection, associationInfo.getAssociateSelfFromOtherModel());
setReverseAssociatedModels(associatedModel, associationInfo, reverseAssociatedModels);
dealAssociatedModelOnManySide(reverseAssociatedModels, baseObj, associatedModel);
} else {
mightClearFKValue(baseObj, associationInfo);
}
}
/**
* When it's on the O side. Get the associated model collection first, then
* iterate all the associated models. Each associated model calls
* {@link #buildBidirectionalAssociations(org.litepal.crud.DataSupport, org.litepal.crud.DataSupport, org.litepal.crud.model.AssociationsInfo)}
* to build bidirectional association if they haven't built yet. Then calls
* {@link #dealAssociatedModelOnOneSide(org.litepal.crud.DataSupport, org.litepal.crud.DataSupport)} to set
* foreign key.
*
* @param baseObj
* The baseObj currently want to persist.
* @param associationInfo
* The associated info analyzed by
* {@link LitePalBase#getAssociationInfo(String)}.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws java.lang.reflect.InvocationTargetException
*/
private void analyzeOneSide(DataSupport baseObj, AssociationsInfo associationInfo)
throws SecurityException, IllegalArgumentException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
Collection<DataSupport> associatedModels = getAssociatedModels(baseObj, associationInfo);
if (associatedModels == null || associatedModels.isEmpty()) {
String tableName = DBUtility.getTableNameByClassName(associationInfo
.getAssociatedClassName());
baseObj.addAssociatedTableNameToClearFK(tableName);
return;
}
for (DataSupport associatedModel : associatedModels) {
buildBidirectionalAssociations(baseObj, associatedModel, associationInfo);
dealAssociatedModelOnOneSide(baseObj, associatedModel);
}
}
/**
* Check if the baseObj is already existed in the associatedModels
* collection. If not add baseObj into the collection. Then if the
* associated model is saved, add its' name and id to baseObj by calling
* {@link org.litepal.crud.DataSupport#addAssociatedModelWithFK(String, long)}.
*
* @param associatedModels
* The associated model collection.
* @param baseObj
* The baseObj currently want to persist.
* @param associatedModel
* The associated info analyzed by
* {@link LitePalBase#getAssociationInfo(String)}.
*/
private void dealAssociatedModelOnManySide(Collection<DataSupport> associatedModels,
DataSupport baseObj, DataSupport associatedModel) {
if (!associatedModels.contains(baseObj)) {
associatedModels.add(baseObj);
}
if (associatedModel.isSaved()) {
baseObj.addAssociatedModelWithoutFK(associatedModel.getTableName(),
associatedModel.getBaseObjId());
}
}
/**
* Deals with associated model on one side.
*
* @param baseObj
* The baseObj currently want to persist.
* @param associatedModel
* The associated info analyzed by
* {@link LitePalBase#getAssociationInfo(String)}.
*/
private void dealAssociatedModelOnOneSide(DataSupport baseObj, DataSupport associatedModel) {
dealsAssociationsOnTheSideWithoutFK(baseObj, associatedModel);
}
}

@ -0,0 +1,127 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud;
import java.lang.reflect.InvocationTargetException;
import org.litepal.LitePalBase;
import org.litepal.crud.model.AssociationsInfo;
import org.litepal.util.DBUtility;
/**
* Deals analysis work when comes to two models are associated with One2One
* associations.
*
* @author Tony Green
* @since 1.1
*/
public class One2OneAnalyzer extends AssociationsAnalyzer {
/**
* Analyzing the AssociationInfo. It will help baseObj assign the necessary
* values automatically. If the two associated models have bidirectional
* associations in class files but developer has only build unidirectional
* associations in models, it will force to build the bidirectional
* associations. Besides
* {@link org.litepal.crud.DataSupport#addAssociatedModelWithFK(String, long)} and
* {@link org.litepal.crud.DataSupport#addAssociatedModelWithoutFK(String, long)} will be
* called here to put right values into tables.
*
* @param baseObj
* The baseObj currently want to persist or update.
* @param associationInfo
* The associated info analyzed by
* {@link LitePalBase#getAssociationInfo(String)}.
* @throws SecurityException
* @throws IllegalArgumentException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws java.lang.reflect.InvocationTargetException
*/
void analyze(DataSupport baseObj, AssociationsInfo associationInfo) throws SecurityException,
IllegalArgumentException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
DataSupport associatedModel = getAssociatedModel(baseObj, associationInfo);
if (associatedModel != null) {
buildBidirectionalAssociations(baseObj, associatedModel, associationInfo);
dealAssociatedModel(baseObj, associatedModel, associationInfo);
} else {
String tableName = DBUtility.getTableNameByClassName(associationInfo
.getAssociatedClassName());
baseObj.addAssociatedTableNameToClearFK(tableName);
}
}
/**
* Check the association type. If it's bidirectional association, calls
* {@link #bidirectionalCondition(org.litepal.crud.DataSupport, org.litepal.crud.DataSupport)}. If it's
* unidirectional association, calls
* {@link #unidirectionalCondition(org.litepal.crud.DataSupport, org.litepal.crud.DataSupport)}.
*
* @param baseObj
* The baseObj currently want to persist.
* @param associatedModel
* The associated model of baseObj.
* @param associationInfo
* The associated info analyzed by
* {@link LitePalBase#getAssociationInfo(String)}.
*/
private void dealAssociatedModel(DataSupport baseObj, DataSupport associatedModel,
AssociationsInfo associationInfo) {
if (associationInfo.getAssociateSelfFromOtherModel() != null) {
bidirectionalCondition(baseObj, associatedModel);
} else {
unidirectionalCondition(baseObj, associatedModel);
}
}
/**
* Deals bidirectional association condition. If associated model is saved,
* add its' name and id to baseObj by calling
* {@link org.litepal.crud.DataSupport#addAssociatedModelWithFK(String, long)}. Add its' name
* and id to baseObj by calling
* {@link org.litepal.crud.DataSupport#addAssociatedModelWithoutFK(String, long)}.
*
* @param baseObj
* The baseObj currently want to persist.
* @param associatedModel
* The associated model of baseObj.
*/
private void bidirectionalCondition(DataSupport baseObj, DataSupport associatedModel) {
if (associatedModel.isSaved()) {
// use to update associated table after saving
baseObj.addAssociatedModelWithFK(associatedModel.getTableName(),
associatedModel.getBaseObjId());
// use to add foreign key value while saving
baseObj.addAssociatedModelWithoutFK(associatedModel.getTableName(),
associatedModel.getBaseObjId());
}
}
/**
* Deals unidirectional associations condition.
*
* @param baseObj
* The baseObj currently want to persist.
* @param associatedModel
* The associated model of baseObj.
*/
private void unidirectionalCondition(DataSupport baseObj, DataSupport associatedModel) {
dealsAssociationsOnTheSideWithoutFK(baseObj, associatedModel);
}
}

@ -0,0 +1,274 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud;
import java.util.List;
import org.litepal.util.BaseUtility;
import org.litepal.util.DBUtility;
import android.database.sqlite.SQLiteDatabase;
/**
* This is a component under DataSupport. It deals with query stuff as primary
* task.
*
* @author Tony Green
* @since 1.1
*/
class QueryHandler extends DataHandler {
/**
* Initialize {@link org.litepal.crud.DataHandler#mDatabase} for operating database. Do not
* allow to create instance of QueryHandler out of CRUD package.
*
* @param db
* The instance of SQLiteDatabase.
*/
QueryHandler(SQLiteDatabase db) {
mDatabase = db;
}
/**
* The open interface for other classes in CRUD package to query a record
* based on id. If the result set is empty, gives null back.
*
* @param modelClass
* Which table to query and the object type to return.
* @param id
* Which record to query.
* @param isEager
* True to load the associated models, false not.
* @return An object with found data from database, or null.
*/
<T> T onFind(Class<T> modelClass, long id, boolean isEager) {
List<T> dataList = query(modelClass, null, "id = ?", new String[] { String.valueOf(id) },
null, null, null, null, getForeignKeyAssociations(modelClass.getName(), isEager));
if (dataList.size() > 0) {
return dataList.get(0);
}
return null;
}
/**
* The open interface for other classes in CRUD package to query the first
* record in a table. If the result set is empty, gives null back.
*
* @param modelClass
* Which table to query and the object type to return.
* @param isEager
* True to load the associated models, false not.
* @return An object with data of first row, or null.
*/
<T> T onFindFirst(Class<T> modelClass, boolean isEager) {
List<T> dataList = query(modelClass, null, null, null, null, null, "id", "1",
getForeignKeyAssociations(modelClass.getName(), isEager));
if (dataList.size() > 0) {
return dataList.get(0);
}
return null;
}
/**
* The open interface for other classes in CRUD package to query the last
* record in a table. If the result set is empty, gives null back.
*
* @param modelClass
* Which table to query and the object type to return.
* @param isEager
* True to load the associated models, false not.
* @return An object with data of last row, or null.
*/
<T> T onFindLast(Class<T> modelClass, boolean isEager) {
List<T> dataList = query(modelClass, null, null, null, null, null, "id desc", "1",
getForeignKeyAssociations(modelClass.getName(), isEager));
if (dataList.size() > 0) {
return dataList.get(0);
}
return null;
}
/**
* The open interface for other classes in CRUD package to query multiple
* records by an id array. Pass no ids means query all rows.
*
* @param modelClass
* Which table to query and the object type to return as a list.
* @param isEager
* True to load the associated models, false not.
* @param ids
* Which records to query. Or do not pass it to find all records.
* @return An object list with found data from database, or an empty list.
*/
<T> List<T> onFindAll(Class<T> modelClass, boolean isEager, long... ids) {
List<T> dataList;
if (isAffectAllLines(ids)) {
dataList = query(modelClass, null, null, null, null, null, "id", null,
getForeignKeyAssociations(modelClass.getName(), isEager));
} else {
dataList = query(modelClass, null, getWhereOfIdsWithOr(ids), null, null, null, "id",
null, getForeignKeyAssociations(modelClass.getName(), isEager));
}
return dataList;
}
/**
* The open interface for other classes in CRUD package to query multiple
* records by parameters.
*
* @param modelClass
* Which table to query and the object type to return as a list.
* @param columns
* A String array of which columns to return. Passing null will
* return all columns.
* @param conditions
* A filter declaring which rows to return, formatted as an SQL
* WHERE clause. Passing null will return all rows.
* @param orderBy
* How to order the rows, formatted as an SQL ORDER BY clause.
* Passing null will use the default sort order, which may be
* unordered.
* @param limit
* Limits the number of rows returned by the query, formatted as
* LIMIT clause.
* @param isEager
* True to load the associated models, false not.
* @return An object list with found data from database, or an empty list.
*/
<T> List<T> onFind(Class<T> modelClass, String[] columns, String[] conditions, String orderBy,
String limit, boolean isEager) {
BaseUtility.checkConditionsCorrect(conditions);
if (conditions != null && conditions.length > 0) {
conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);
}
orderBy = DBUtility.convertOrderByClauseToValidName(orderBy);
return query(modelClass, columns, getWhereClause(conditions),
getWhereArgs(conditions), null, null, orderBy, limit,
getForeignKeyAssociations(modelClass.getName(), isEager));
}
/**
* The open interface for other classes in CRUD package to Count the
* records.
*
* @param tableName
* Which table to query from.
* @param conditions
* A filter declaring which rows to return, formatted as an SQL
* WHERE clause. Passing null will return all rows.
* @return Count of the specified table.
*/
int onCount(String tableName, String[] conditions) {
BaseUtility.checkConditionsCorrect(conditions);
if (conditions != null && conditions.length > 0) {
conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);
}
return mathQuery(tableName, new String[] { "count(1)" }, conditions, int.class);
}
/**
* The open interface for other classes in CRUD package to calculate the
* average value on a given column.
*
* @param tableName
* Which table to query from.
* @param column
* The based on column to calculate.
* @param conditions
* A filter declaring which rows to return, formatted as an SQL
* WHERE clause. Passing null will return all rows.
* @return The average value on a given column.
*/
double onAverage(String tableName, String column, String[] conditions) {
BaseUtility.checkConditionsCorrect(conditions);
if (conditions != null && conditions.length > 0) {
conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);
}
return mathQuery(tableName, new String[] { "avg(" + column + ")" }, conditions,
double.class);
}
/**
* The open interface for other classes in CRUD package to calculate the
* maximum value on a given column.
*
* @param tableName
* Which table to query from.
* @param column
* The based on column to calculate.
* @param conditions
* A filter declaring which rows to return, formatted as an SQL
* WHERE clause. Passing null will return all rows.
* @param type
* The type of the based on column.
* @return The maximum value on a given column.
*/
<T> T onMax(String tableName, String column, String[] conditions, Class<T> type) {
BaseUtility.checkConditionsCorrect(conditions);
if (conditions != null && conditions.length > 0) {
conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);
}
return mathQuery(tableName, new String[] { "max(" + column + ")" }, conditions, type);
}
/**
* The open interface for other classes in CRUD package to calculate the
* minimum value on a given column.
*
* @param tableName
* Which table to query from.
* @param column
* The based on column to calculate.
* @param conditions
* A filter declaring which rows to return, formatted as an SQL
* WHERE clause. Passing null will return all rows.
* @param type
* The type of the based on column.
* @return The minimum value on a given column.
*/
<T> T onMin(String tableName, String column, String[] conditions, Class<T> type) {
BaseUtility.checkConditionsCorrect(conditions);
if (conditions != null && conditions.length > 0) {
conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);
}
return mathQuery(tableName, new String[] { "min(" + column + ")" }, conditions, type);
}
/**
* The open interface for other classes in CRUD package to calculate the sum
* of values on a given column.
*
* @param tableName
* Which table to query from.
* @param column
* The based on column to calculate.
* @param conditions
* A filter declaring which rows to return, formatted as an SQL
* WHERE clause. Passing null will return all rows.
* @param type
* The type of the based on column.
* @return The sum value on a given column.
*/
<T> T onSum(String tableName, String column, String[] conditions, Class<T> type) {
BaseUtility.checkConditionsCorrect(conditions);
if (conditions != null && conditions.length > 0) {
conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);
}
return mathQuery(tableName, new String[] { "sum(" + column + ")" }, conditions, type);
}
}

@ -0,0 +1,582 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import org.litepal.crud.model.AssociationsInfo;
import org.litepal.exceptions.DataSupportException;
import org.litepal.util.DBUtility;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.litepal.util.BaseUtility.changeCase;
/**
* This is a component under DataSupport. It deals with the saving stuff as
* primary task. All the implementation based on the java reflection API and
* Android SQLiteDatabase API. It will persist the model class into table. If
* there're some associated models already persisted, it will build the
* associations in database automatically between the current model and the
* associated models.
*
* @author Tony Green
* @since 1.1
*/
class SaveHandler extends DataHandler {
/**
* indicates that associations can be ignored while saving.
*/
private boolean ignoreAssociations = false;
private ContentValues values;
/**
* Initialize {@link org.litepal.crud.DataHandler#mDatabase} for operating database. Do not
* allow to create instance of SaveHandler out of CRUD package.
*
* @param db
* The instance of SQLiteDatabase.
*/
SaveHandler(SQLiteDatabase db) {
values = new ContentValues();
mDatabase = db;
}
/**
* The open interface for other classes in CRUD package to save a model. It
* is called when a model class calls the save method. First of all, the
* passed in baseObj will be saved into database. Then LitePal will analyze
* the associations. If there're associated models detected, each associated
* model which is persisted will build association with current model in
* database.
*
* @param baseObj
* Current model to persist.
* @throws java.lang.reflect.InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws SecurityException
*/
void onSave(DataSupport baseObj) throws SecurityException, IllegalArgumentException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException {
String className = baseObj.getClassName();
List<Field> supportedFields = getSupportedFields(className);
List<Field> supportedGenericFields = getSupportedGenericFields(className);
Collection<AssociationsInfo> associationInfos = getAssociationInfo(className);
if (!baseObj.isSaved()) {
if (!ignoreAssociations) {
analyzeAssociatedModels(baseObj, associationInfos);
}
doSaveAction(baseObj, supportedFields, supportedGenericFields);
if (!ignoreAssociations) {
analyzeAssociatedModels(baseObj, associationInfos);
}
} else {
if (!ignoreAssociations) {
analyzeAssociatedModels(baseObj, associationInfos);
}
doUpdateAction(baseObj, supportedFields, supportedGenericFields);
}
}
/**
* The open interface for other classes in CRUD package to save a model. It
* is called when a model class calls the save method. This method will only
* save the baseObj into database without analyzing any association, so that
* the saving process will be faster.
*
* @param baseObj
* Current model to persist.
* @throws java.lang.reflect.InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws SecurityException
*/
void onSaveFast(DataSupport baseObj) throws SecurityException, IllegalArgumentException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException {
ignoreAssociations = true;
onSave(baseObj);
}
/**
* The open interface for other classes in CRUD package to save a model
* collection. It is called when developer calls
* {@link org.litepal.crud.DataSupport#saveAll(java.util.Collection)}. Each model in the collection
* will be persisted. If there're associated models detected, each
* associated model which is persisted will build association with current
* model in database.
*
* @param collection
* Holds all models to persist.
* @throws java.lang.reflect.InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws SecurityException
*/
<T extends DataSupport> void onSaveAll(Collection<T> collection) throws SecurityException,
IllegalArgumentException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
if (collection != null && collection.size() > 0) {
DataSupport[] array = collection.toArray(new DataSupport[0]);
DataSupport firstObj = array[0];
String className = firstObj.getClassName();
List<Field> supportedFields = getSupportedFields(className);
List<Field> supportedGenericFields = getSupportedGenericFields(className);
Collection<AssociationsInfo> associationInfos = getAssociationInfo(className);
for (DataSupport baseObj : array) {
if (!baseObj.isSaved()) {
analyzeAssociatedModels(baseObj, associationInfos);
doSaveAction(baseObj, supportedFields, supportedGenericFields);
analyzeAssociatedModels(baseObj, associationInfos);
} else {
analyzeAssociatedModels(baseObj, associationInfos);
doUpdateAction(baseObj, supportedFields, supportedGenericFields);
}
baseObj.clearAssociatedData();
}
}
}
/**
* Persisting model class into database happens here. But first
* {@link #beforeSave(org.litepal.crud.DataSupport, java.util.List, android.content.ContentValues)} will be called to
* put the values for ContentValues. When the model is saved,
* {@link #afterSave(org.litepal.crud.DataSupport, java.util.List, java.util.List, long)} will be called to do stuffs
* after model is saved. Note that SaveSupport won't help with id. Any
* developer who wants to set value to id will be ignored here. The value of
* id will be generated by SQLite automatically.
*
* @param baseObj
* Current model to persist.
* @param supportedFields
* List of all supported fields.
* @param supportedGenericFields
* List of all supported generic fields.
* @throws java.lang.reflect.InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws SecurityException
*/
private void doSaveAction(DataSupport baseObj, List<Field> supportedFields, List<Field> supportedGenericFields)
throws SecurityException, IllegalArgumentException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
values.clear();
beforeSave(baseObj, supportedFields, values);
long id = saving(baseObj, values);
afterSave(baseObj, supportedFields, supportedGenericFields, id);
}
/**
* Before the self model is saved, it will be analyzed first. Put all the
* data contained by the model into ContentValues, including the fields
* value and foreign key value.
*
* @param baseObj
* Current model to persist.
* @param supportedFields
* List of all supported fields.
* @param values
* To store data of current model for persisting.
* @throws java.lang.reflect.InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws SecurityException
*/
private void beforeSave(DataSupport baseObj, List<Field> supportedFields, ContentValues values)
throws SecurityException, IllegalArgumentException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
putFieldsValue(baseObj, supportedFields, values);
if (!ignoreAssociations) {
putForeignKeyValue(values, baseObj);
}
}
/**
* Calling {@link android.database.sqlite.SQLiteDatabase#insert(String, String, android.content.ContentValues)} to
* persist the current model.
*
* @param baseObj
* Current model to persist.
* @param values
* To store data of current model for persisting.
* @return The row ID of the newly inserted row, or -1 if an error occurred.
*/
private long saving(DataSupport baseObj, ContentValues values) {
if (values.size() == 0) {
values.putNull("id");
}
return mDatabase.insert(baseObj.getTableName(), null, values);
}
/**
* After the model is saved, do the extra work that need to do.
*
* @param baseObj
* Current model that is persisted.
* @param supportedFields
* List of all supported fields.
* @param supportedGenericFields
* List of all supported generic fields.
* @param id
* The current model's id.
*/
private void afterSave(DataSupport baseObj, List<Field> supportedFields,
List<Field> supportedGenericFields, long id) throws IllegalAccessException, InvocationTargetException {
throwIfSaveFailed(id);
assignIdValue(baseObj, getIdField(supportedFields), id);
updateGenericTables(baseObj, supportedGenericFields, id);
if (!ignoreAssociations) {
updateAssociatedTableWithFK(baseObj);
insertIntermediateJoinTableValue(baseObj, false);
}
}
/**
* When a model is associated with two different models.
*
* @param baseObj
* The class of base object.
* @param supportedFields
* List of all supported fields.
* @param supportedGenericFields
* List of all supported generic fields.
* @throws java.lang.reflect.InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws SecurityException
*/
private void doUpdateAction(DataSupport baseObj, List<Field> supportedFields, List<Field> supportedGenericFields)
throws SecurityException, IllegalArgumentException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
values.clear();
beforeUpdate(baseObj, supportedFields, values);
updating(baseObj, values);
afterUpdate(baseObj, supportedGenericFields);
}
/**
* Before updating model, it will be analyzed first. Put all the data
* contained by the model into ContentValues, including the fields value and
* foreign key value. If the associations between models has been removed.
* The foreign key value in database should be cleared too.
*
* @param baseObj
* Current model to update.
* @param supportedFields
* List of all supported fields.
* @param values
* To store data of current model for updating.
* @throws java.lang.reflect.InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws SecurityException
*/
private void beforeUpdate(DataSupport baseObj, List<Field> supportedFields, ContentValues values)
throws SecurityException, IllegalArgumentException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
putFieldsValue(baseObj, supportedFields, values);
if (!ignoreAssociations) {
putForeignKeyValue(values, baseObj);
for (String fkName : baseObj.getListToClearSelfFK()) {
values.putNull(fkName);
}
}
}
/**
* Calling
* {@link android.database.sqlite.SQLiteDatabase#update(String, android.content.ContentValues, String, String[])} to
* update the current model.
*
* @param baseObj
* Current model to update.
* @param values
* To store data of current model for updating.
*/
private void updating(DataSupport baseObj, ContentValues values) {
mDatabase.update(baseObj.getTableName(), values, "id = ?",
new String[] { String.valueOf(baseObj.getBaseObjId()) });
}
/**
* After the model is updated, do the extra work that need to do.
*
* @param baseObj
* Current model that is updated.
* @param supportedGenericFields
* List of all supported generic fields.
*/
private void afterUpdate(DataSupport baseObj, List<Field> supportedGenericFields)
throws InvocationTargetException, IllegalAccessException {
updateGenericTables(baseObj, supportedGenericFields, baseObj.getBaseObjId());
if (!ignoreAssociations) {
updateAssociatedTableWithFK(baseObj);
insertIntermediateJoinTableValue(baseObj, true);
clearFKValueInAssociatedTable(baseObj);
}
}
/**
* Get the id field by the passed in field list.
*
* @param supportedFields
* The field list to find from.
* @return The id field. If not found one return null.
*/
private Field getIdField(List<Field> supportedFields) {
for (Field field : supportedFields) {
if (isIdColumn(field.getName())) {
return field;
}
}
return null;
}
/**
* If the model saved failed, throw an exception.
*
* @param id
* The id returned by SQLite. -1 means saved failed.
*/
private void throwIfSaveFailed(long id) {
if (id == -1) {
throw new DataSupportException(DataSupportException.SAVE_FAILED);
}
}
/**
* Assign the generated id value to the model. The
* {@link org.litepal.crud.DataSupport#baseObjId} will be assigned anyway. If the model has a
* field named id or _id, LitePal will assign it too. The
* {@link org.litepal.crud.DataSupport#baseObjId} will be used as identify of this model for
* system use. The id or _id field will help developers for their own
* purpose.
*
* @param baseObj
* Current model that is persisted.
* @param idField
* The field of id.
* @param id
* The value of id.
*/
private void assignIdValue(DataSupport baseObj, Field idField, long id) {
try {
giveBaseObjIdValue(baseObj, id);
if (idField != null) {
giveModelIdValue(baseObj, idField.getName(), idField.getType(), id);
}
} catch (Exception e) {
throw new DataSupportException(e.getMessage(), e);
}
}
/**
* After saving a model, the id for this model will be returned. Assign this
* id to the model's id or _id field if it exists.
*
* @param baseObj
* The class of base object.
* @param idName
* The name of id. Only id or _id is valid.
* @param idType
* The type of id. Only int or long is valid.
* @param id
* The value of id.
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
private void giveModelIdValue(DataSupport baseObj, String idName, Class<?> idType, long id)
throws SecurityException, NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
if (shouldGiveModelIdValue(idName, idType, id)) {
Object value;
if (idType == int.class || idType == Integer.class) {
value = (int) id;
} else if (idType == long.class || idType == Long.class) {
value = id;
} else {
throw new DataSupportException(DataSupportException.ID_TYPE_INVALID_EXCEPTION);
}
DynamicExecutor.setField(baseObj, idName, value, baseObj.getClass());
}
}
/**
* If the table for this model have a foreign key column, the value of
* foreign key id should be saved too.
*
* @param values
* The instance of ContentValues to put foreign key value.
*/
private void putForeignKeyValue(ContentValues values, DataSupport baseObj) {
Map<String, Long> associatedModelMap = baseObj.getAssociatedModelsMapWithoutFK();
for (String associatedTableName : associatedModelMap.keySet()) {
values.put(getForeignKeyColumnName(associatedTableName),
associatedModelMap.get(associatedTableName));
}
}
/**
* Update the foreign keys in the associated model's table.
*
* @param baseObj
* Current model that is persisted.
*/
private void updateAssociatedTableWithFK(DataSupport baseObj) {
Map<String, Set<Long>> associatedModelMap = baseObj.getAssociatedModelsMapWithFK();
ContentValues values = new ContentValues();
for (String associatedTableName : associatedModelMap.keySet()) {
values.clear();
String fkName = getForeignKeyColumnName(baseObj.getTableName());
values.put(fkName, baseObj.getBaseObjId());
Set<Long> ids = associatedModelMap.get(associatedTableName);
if (ids != null && !ids.isEmpty()) {
mDatabase.update(associatedTableName, values, getWhereOfIdsWithOr(ids), null);
}
}
}
/**
* When the associations breaks between current model and associated models,
* clear all the associated models' foreign key value if it exists.
*
* @param baseObj
* Current model that is persisted.
*/
private void clearFKValueInAssociatedTable(DataSupport baseObj) {
List<String> associatedTableNames = baseObj.getListToClearAssociatedFK();
for (String associatedTableName : associatedTableNames) {
String fkColumnName = getForeignKeyColumnName(baseObj.getTableName());
ContentValues values = new ContentValues();
values.putNull(fkColumnName);
String whereClause = fkColumnName + " = " + baseObj.getBaseObjId();
mDatabase.update(associatedTableName, values, whereClause, null);
}
}
/**
* Insert values into intermediate join tables for self model and associated
* models.
*
* @param baseObj
* Current model that is persisted.
* @param isUpdate
* The current action is update or not.
*/
private void insertIntermediateJoinTableValue(DataSupport baseObj, boolean isUpdate) {
Map<String, Set<Long>> associatedIdsM2M = baseObj.getAssociatedModelsMapForJoinTable();
ContentValues values = new ContentValues();
for (String associatedTableName : associatedIdsM2M.keySet()) {
String joinTableName = getIntermediateTableName(baseObj, associatedTableName);
if (isUpdate) {
mDatabase.delete(joinTableName, getWhereForJoinTableToDelete(baseObj),
new String[] { String.valueOf(baseObj.getBaseObjId()) });
}
Set<Long> associatedIdsM2MSet = associatedIdsM2M.get(associatedTableName);
for (long associatedId : associatedIdsM2MSet) {
values.clear();
values.put(getForeignKeyColumnName(baseObj.getTableName()), baseObj.getBaseObjId());
values.put(getForeignKeyColumnName(associatedTableName), associatedId);
mDatabase.insert(joinTableName, null, values);
}
}
}
/**
* Get the where clause to delete intermediate join table's value for
* updating.
*
* @param baseObj
* Current model that is persisted.
* @return The where clause to execute.
*/
private String getWhereForJoinTableToDelete(DataSupport baseObj) {
StringBuilder where = new StringBuilder();
where.append(getForeignKeyColumnName(baseObj.getTableName()));
where.append(" = ?");
return where.toString();
}
/**
* Judge should assign id value to model's id field. The principle is that
* if id name is not null, id type is not null and id is greater than 0,
* then should assign id value to it.
*
* @param idName
* The name of id field.
* @param idType
* The type of id field.
* @param id
* The value of id.
* @return If id name is not null, id type is not null and id is greater
* than 0, return true. Otherwise return false.
*/
private boolean shouldGiveModelIdValue(String idName, Class<?> idType, long id) {
return idName != null && idType != null && id > 0;
}
/**
* Update the generic data in generic tables. Need to delete the related generic data before
* saving, because generic data has no id.
* @param baseObj
* Current model that is persisted.
*@param supportedGenericFields
* List of all supported generic fields.
* @param id
* The id of current model.
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private void updateGenericTables(DataSupport baseObj, List<Field> supportedGenericFields,
long id) throws IllegalAccessException, InvocationTargetException {
for (Field field : supportedGenericFields) {
field.setAccessible(true);
Collection<?> collection = (Collection<?>) field.get(baseObj);
if (collection != null) {
String tableName = DBUtility.getGenericTableName(baseObj.getClassName(), field.getName());
String genericValueIdColumnName = DBUtility.getGenericValueIdColumnName(baseObj.getClassName());
mDatabase.delete(tableName, genericValueIdColumnName + " = ?", new String[] {String.valueOf(id)});
for (Object object : collection) {
ContentValues values = new ContentValues();
values.put(genericValueIdColumnName, id);
Object[] parameters = new Object[] { changeCase(DBUtility.convertToValidColumnName(field.getName())), object };
Class<?>[] parameterTypes = new Class[] { String.class, getGenericTypeClass(field) };
DynamicExecutor.send(values, "put", parameters, values.getClass(), parameterTypes);
mDatabase.insert(tableName, null, values);
}
}
}
}
}

@ -0,0 +1,403 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import org.litepal.crud.model.AssociationsInfo;
import org.litepal.exceptions.DataSupportException;
import org.litepal.util.BaseUtility;
import org.litepal.util.DBUtility;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.litepal.util.BaseUtility.changeCase;
/**
* This is a component under DataSupport. It deals with the updating stuff as
* primary task. Either updating specifying data with id or updating multiple
* lines with conditions can be done here.
*
* @author Tony Green
* @since 1.1
*/
class UpdateHandler extends DataHandler {
/**
* Initialize {@link org.litepal.crud.DataHandler#mDatabase} for operating database. Do not
* allow to create instance of UpdateHandler out of CRUD package.
*
* @param db
* The instance of SQLiteDatabase.
*/
UpdateHandler(SQLiteDatabase db) {
mDatabase = db;
}
/**
* The open interface for other classes in CRUD package to update. Using
* baseObj to decide which table to update, and id to decide a specific row.
* The value that need to update is stored in baseObj.
*
* @param baseObj
* Which table to update by model instance.
* @param id
* Which record to update.
* @return The number of rows affected.
* @throws java.lang.reflect.InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws SecurityException
*/
int onUpdate(DataSupport baseObj, long id) throws SecurityException, IllegalArgumentException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException {
List<Field> supportedFields = getSupportedFields(baseObj.getClassName());
List<Field> supportedGenericFields = getSupportedGenericFields(baseObj.getClassName());
updateGenericTables(baseObj, supportedGenericFields, id);
ContentValues values = new ContentValues();
putFieldsValue(baseObj, supportedFields, values);
putFieldsToDefaultValue(baseObj, values, id);
if (values.size() > 0) {
return mDatabase.update(baseObj.getTableName(), values, "id = " + id, null);
}
return 0;
}
/**
* The open interface for other classes in CRUD package to update. Using
* modelClass to decide which table to update, and id to decide a specific
* row. The value that need to update is stored in ContentValues.
*
* @param modelClass
* Which table to update by class.
* @param id
* Which record to update.
* @param values
* A map from column names to new column values. null is a valid
* value that will be translated to NULL.
* @return The number of rows affected.
*/
int onUpdate(Class<?> modelClass, long id, ContentValues values) {
if (values.size() > 0) {
convertContentValues(values);
return mDatabase.update(getTableName(modelClass), values, "id = " + id, null);
}
return 0;
}
/**
* The open interface for other classes in CRUD package to update multiple
* rows. Using baseObj to decide which table to update, and conditions
* representing the WHERE part of an SQL statement. The value that need to
* update is stored in baseObj.
*
* @param baseObj
* Which table to update by model instance.
* @param conditions
* A string array representing the WHERE part of an SQL
* statement.
* @return The number of rows affected.
* @throws java.lang.reflect.InvocationTargetException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws SecurityException
*/
@SuppressWarnings("unchecked")
int onUpdateAll(DataSupport baseObj, String... conditions) throws SecurityException,
IllegalArgumentException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
BaseUtility.checkConditionsCorrect(conditions);
if (conditions != null && conditions.length > 0) {
conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);
}
List<Field> supportedFields = getSupportedFields(baseObj.getClassName());
List<Field> supportedGenericFields = getSupportedGenericFields(baseObj.getClassName());
long[] ids = null;
if (!supportedGenericFields.isEmpty()) {
List<DataSupport> list = (List<DataSupport>) DataSupport.select("id").where(conditions).find(baseObj.getClass());
if (list.size() > 0) {
ids = new long[list.size()];
for (int i = 0; i < ids.length; i++) {
DataSupport dataSupport = list.get(i);
ids[i] = dataSupport.getBaseObjId();
}
updateGenericTables(baseObj, supportedGenericFields, ids);
}
}
ContentValues values = new ContentValues();
putFieldsValue(baseObj, supportedFields, values);
putFieldsToDefaultValue(baseObj, values, ids);
return doUpdateAllAction(baseObj.getTableName(), values, conditions);
}
/**
* The open interface for other classes in CRUD package to update multiple
* rows. Using tableName to decide which table to update, and conditions
* representing the WHERE part of an SQL statement. The value that need to
* update is stored in ContentValues.
*
* @param tableName
* Which table to update.
* @param conditions
* A string array representing the WHERE part of an SQL
* statement.
* @param values
* A map from column names to new column values. null is a valid
* value that will be translated to NULL.
* @return The number of rows affected.
*/
int onUpdateAll(String tableName, ContentValues values, String... conditions) {
BaseUtility.checkConditionsCorrect(conditions);
if (conditions != null && conditions.length > 0) {
conditions[0] = DBUtility.convertWhereClauseToColumnName(conditions[0]);
}
convertContentValues(values);
return doUpdateAllAction(tableName, values, conditions);
}
/**
* Do the action for updating multiple rows. It will check the validity of
* conditions, then update rows in database. If the format of conditions is
* invalid, throw DataSupportException.
*
* @param tableName
* Which table to delete from.
* @param conditions
* A string array representing the WHERE part of an SQL
* statement.
* @param values
* A map from column names to new column values. null is a valid
* value that will be translated to NULL.
* @return The number of rows affected.
*/
private int doUpdateAllAction(String tableName, ContentValues values, String... conditions) {
BaseUtility.checkConditionsCorrect(conditions);
if (values.size() > 0) {
return mDatabase.update(tableName, values, getWhereClause(conditions),
getWhereArgs(conditions));
}
return 0;
}
/**
* Iterate all the fields that need to set to default value. If the field is
* id, ignore it. Or put the default value of field into ContentValues.
*
* @param baseObj
* Which table to update by model instance.
* @param values
* To store data of current model for persisting or updating.
* @param ids
* The id array of query result.
*/
private void putFieldsToDefaultValue(DataSupport baseObj, ContentValues values, long... ids) {
String fieldName = null;
try {
DataSupport emptyModel = getEmptyModel(baseObj);
Class<?> emptyModelClass = emptyModel.getClass();
for (String name : baseObj.getFieldsToSetToDefault()) {
if (!isIdColumn(name)) {
fieldName = name;
Field field = emptyModelClass.getDeclaredField(fieldName);
if (isCollection(field.getType())) {
if (ids != null && ids.length > 0) {
String genericTypeName = getGenericTypeName(field);
if (BaseUtility.isGenericTypeSupported(genericTypeName)) {
String tableName = DBUtility.getGenericTableName(baseObj.getClassName(), field.getName());
String genericValueIdColumnName = DBUtility.getGenericValueIdColumnName(baseObj.getClassName());
StringBuilder whereClause = new StringBuilder();
boolean needOr = false;
for (long id : ids) {
if (needOr) {
whereClause.append(" or ");
}
whereClause.append(genericValueIdColumnName).append(" = ").append(id);
needOr = true;
}
mDatabase.delete(tableName, whereClause.toString(), null);
}
}
} else {
putContentValuesForUpdate(emptyModel, field, values);
}
}
}
} catch (NoSuchFieldException e) {
throw new DataSupportException(DataSupportException.noSuchFieldExceptioin(
baseObj.getClassName(), fieldName), e);
} catch (Exception e) {
throw new DataSupportException(e.getMessage(), e);
}
}
/**
* Unused currently.
*/
@SuppressWarnings("unused")
private int doUpdateAssociations(DataSupport baseObj, long id, ContentValues values) {
int rowsAffected = 0;
analyzeAssociations(baseObj);
updateSelfTableForeignKey(baseObj, values);
rowsAffected += updateAssociatedTableForeignKey(baseObj, id);
return rowsAffected;
}
/**
* Analyze the associations of baseObj and store the result in it. The
* associations will be used when deleting referenced data of baseObj.
* Unused currently.
*
* @param baseObj
* The record to update.
*/
private void analyzeAssociations(DataSupport baseObj) {
try {
Collection<AssociationsInfo> associationInfos = getAssociationInfo(baseObj
.getClassName());
analyzeAssociatedModels(baseObj, associationInfos);
} catch (Exception e) {
throw new DataSupportException(e.getMessage(), e);
}
}
/**
* Unused currently.
*/
private void updateSelfTableForeignKey(DataSupport baseObj, ContentValues values) {
Map<String, Long> associatedModelMap = baseObj.getAssociatedModelsMapWithoutFK();
for (String associatedTable : associatedModelMap.keySet()) {
String fkName = getForeignKeyColumnName(associatedTable);
values.put(fkName, associatedModelMap.get(associatedTable));
}
}
/**
* Unused currently.
*/
private int updateAssociatedTableForeignKey(DataSupport baseObj, long id) {
Map<String, Set<Long>> associatedModelMap = baseObj.getAssociatedModelsMapWithFK();
ContentValues values = new ContentValues();
for (String associatedTable : associatedModelMap.keySet()) {
values.clear();
String fkName = getForeignKeyColumnName(baseObj.getTableName());
values.put(fkName, id);
Set<Long> ids = associatedModelMap.get(associatedTable);
if (ids != null && !ids.isEmpty()) {
return mDatabase.update(associatedTable, values, getWhereOfIdsWithOr(ids), null);
}
}
return 0;
}
/**
* Update the generic data in generic tables. Need to delete the related generic data before
* saving, because generic data has no id. If generic collection is null or empty, the operation
* will be abort. Clear generic collection data while updating should use {@link DataSupport#setToDefault(String)}
* method.
* @param baseObj
* Current model that is persisted.
*@param supportedGenericFields
* List of all supported generic fields.
* @param ids
* The id array of models.
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private void updateGenericTables(DataSupport baseObj, List<Field> supportedGenericFields,
long... ids) throws IllegalAccessException, InvocationTargetException {
if (ids != null && ids.length > 0) {
for (Field field : supportedGenericFields) {
field.setAccessible(true);
Collection<?> collection = (Collection<?>) field.get(baseObj);
if (collection != null && !collection.isEmpty()) {
String tableName = DBUtility.getGenericTableName(baseObj.getClassName(), field.getName());
String genericValueIdColumnName = DBUtility.getGenericValueIdColumnName(baseObj.getClassName());
for (long id : ids) {
mDatabase.delete(tableName, genericValueIdColumnName + " = ?", new String[] {String.valueOf(id)});
for (Object object : collection) {
ContentValues values = new ContentValues();
values.put(genericValueIdColumnName, id);
Object[] parameters = new Object[] { DBUtility.convertToValidColumnName(changeCase(field.getName())), object };
Class<?>[] parameterTypes = new Class[] { String.class, getGenericTypeClass(field) };
DynamicExecutor.send(values, "put", parameters, values.getClass(), parameterTypes);
mDatabase.insert(tableName, null, values);
}
}
}
}
}
}
/**
* The keys in ContentValues may be put as valid in Java but invalid in database. So convert
* them into valid keys.
* @param values
* A map from column names to new column values. null is a valid
* value that will be translated to NULL.
*/
private void convertContentValues(ContentValues values) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
Map<String, Object> valuesToConvert = new HashMap<String, Object>();
for (String key : values.keySet()) {
if (DBUtility.isFieldNameConflictWithSQLiteKeywords(key)) {
Object object = values.get(key);
valuesToConvert.put(key, object);
}
}
for (String key : valuesToConvert.keySet()) {
String convertedKey = DBUtility.convertToValidColumnName(key);
Object object = values.get(key);
values.remove(key);
if (object == null) {
values.putNull(convertedKey);
} else {
String className = object.getClass().getName();
if ("java.lang.Byte".equals(className)) {
values.put(convertedKey, (Byte) object);
} else if ("[B".equals(className)) {
values.put(convertedKey, (byte[]) object);
} else if ("java.lang.Boolean".equals(className)) {
values.put(convertedKey, (Boolean) object);
} else if ("java.lang.String".equals(className)) {
values.put(convertedKey, (String) object);
} else if ("java.lang.Float".equals(className)) {
values.put(convertedKey, (Float) object);
} else if ("java.lang.Long".equals(className)) {
values.put(convertedKey, (Long) object);
} else if ("java.lang.Integer".equals(className)) {
values.put(convertedKey, (Integer) object);
} else if ("java.lang.Short".equals(className)) {
values.put(convertedKey, (Short) object);
} else if ("java.lang.Double".equals(className)) {
values.put(convertedKey, (Double) object);
}
}
}
}
}
}

@ -0,0 +1,49 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud.async;
/**
* A simple async executor to run tasks in background thread.
*
* @author Tony Green
* @since 2017/2/22
*/
public abstract class AsyncExecutor {
/**
* Task that pending to run.
*/
private Runnable pendingTask;
/**
* Submit a task for pending executing.
* @param task
* The task with specific database operation.
*/
public void submit(Runnable task) {
pendingTask = task;
}
/**
* Run the pending task in background thread.
*/
void execute() {
if (pendingTask != null) {
new Thread(pendingTask).start();
}
}
}

@ -0,0 +1,44 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud.async;
import org.litepal.crud.callback.AverageCallback;
/**
* Executor for average query in background.
*
* @author Tony Green
* @since 2017/2/22
*/
public class AverageExecutor extends AsyncExecutor {
private AverageCallback cb;
/**
* Register a callback listener and async task will start executing right away.
* @param callback
* Callback for average query in background.
*/
public void listen(AverageCallback callback) {
cb = callback;
execute();
}
public AverageCallback getListener() {
return cb;
}
}

@ -0,0 +1,44 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud.async;
import org.litepal.crud.callback.CountCallback;
/**
* Executor for count query in background.
*
* @author Tony Green
* @since 2017/2/22
*/
public class CountExecutor extends AsyncExecutor {
private CountCallback cb;
/**
* Register a callback listener and async task will start executing right away.
* @param callback
* Callback for count query in background.
*/
public void listen(CountCallback callback) {
cb = callback;
execute();
}
public CountCallback getListener() {
return cb;
}
}

@ -0,0 +1,44 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud.async;
import org.litepal.crud.callback.FindCallback;
/**
* Executor for find record in background.
*
* @author Tony Green
* @since 2017/2/22
*/
public class FindExecutor extends AsyncExecutor {
private FindCallback cb;
/**
* Register a callback listener and async task will start executing right away.
* @param callback
* Callback for find record in background.
*/
public void listen(FindCallback callback) {
cb = callback;
execute();
}
public FindCallback getListener() {
return cb;
}
}

@ -0,0 +1,44 @@
/*
* Copyright (C) Tony Green, LitePal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.crud.async;
import org.litepal.crud.callback.FindMultiCallback;
/**
* Executor for find multiple records in background.
*
* @author Tony Green
* @since 2017/2/22
*/
public class FindMultiExecutor extends AsyncExecutor {
private FindMultiCallback cb;
/**
* Register a callback listener and async task will start executing right away.
* @param callback
* Callback for find multiple records in background.
*/
public void listen(FindMultiCallback callback) {
cb = callback;
execute();
}
public FindMultiCallback getListener() {
return cb;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save