Summary code tips in Android.
内容是我自己平时学习与工作积累的代码与准则,并没有什么原理剖析。如有错误欢迎指正,如有侵权,请联系我删除。
android:scrollbars="vertical"
android:fadeScrollbars="false"
//设置item项的子控件能够获得焦点(默认为false,即默认item项的子控件是不能获得焦点的)
listView.setItemsCanFocus(true);
mPopUpWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));
PopUpWindow mPopTextSettingWindow ;
private void showPopupWindow(View targetView) {
//TODO do something
...
int[] location = new int[2];
targetView.getLocationOnScreen(location);
int w = mPopTextSettingWindow.getWidth();
int h = mPopTextSettingWindow.getHeight();
//targetView正上方弹出
mPopTextSettingWindow.showAtLocation(targetView, Gravity.NO_GRAVITY,
location[0] - w / 2 + targetView.getWidth() / 2, location[1] - h -10);
}
//打开关闭触摸声音
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SOUND_EFFECTS_ENABLED, isBeepSound ? 0 : 1);
<blink
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Blink Layout"></TextView>
</blink>
WallpaperManager mWallManager = WallpaperManager.getInstance(getActivity());
mWallManager.suggestDesiredDimensions(wallpaperWidthPX,wallpaperHeightPX);
Intent intent = new Intent();
intent.setAction(Intent.ACTION_WALLPAPER_CHANGED);
mWallManager.setBitmap(mBitmap);//还有setDrawable()等方法。
mContext.sendBroadcast(intent);
WallpaperManager.getInstance(getContext()).getDrawable();
android:imeOptions="actionNext"
android:singleLine="true"
android:nextFocusForward="@+id/et_address"
/**
*
* @author RealMo
* VolumeObserver 主要音量变化(遥控)
*/
private class VolumeObserver extends ContentObserver {
public VolumeObserver(ContentResolver resolver) {
super(new Handler());
}
@Override
public void onChange(boolean selfChange) {
// TODO Auto-generated method stub
super.onChange(selfChange);
Log.d("realmo", "VolumeObserver===========");
isMute = false;
setMuteViewImage(isMute);
// 更新音量条进度
currentVolume = mAudiomanager
.getStreamVolume(AudioManager.STREAM_MUSIC); // 获取当前值
//TODO do something
}
}
//init
VolumeObserver mVolObserver = new VolumeObserver(mContext.getContentResolver());
//监听相应的数据库内容
mContext.getContentResolver().registerContentObserver(
Uri.parse("content://mstar.tv.usersetting/soundsetting/Volume")
, true, mVolObserver);
mHandlerThread.getLooper().quit();
Intent intent= new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
// 最后通知图库更新
intent.setData(Uri.fromFile(file));
sendBroadcast(intent);
private void notifyFileSystemChanged(String filePath) {
if (filePath == null){
return;
}
File f = new File(filePath);
Intent intent;
if (f.isDirectory()) {
intent = new Intent(Intent.ACTION_MEDIA_MOUNTED);
intent.setClassName("com.android.providers.media", "com.android.providers.media.MediaScannerReceiver");
intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));
} else {
//test not work in some devices
intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(new File(path)));
}
context.sendBroadcast(intent);
}
//广播监听Home,并不能拦截Home键
//register
registerReceiver(mHomeKeyEventReceiver, new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
//unregister
unregisterReceiver(mHomeKeyEventReceiver);
//init broadcastreceiver
private BroadcastReceiver mHomeKeyEventReceiver = new BroadcastReceiver() {
String SYSTEM_REASON = "reason";
String SYSTEM_HOME_KEY = "homekey";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
//TODO your task
}
}
};
//设置英语为系统语言
Locale locale = new Locale("es", "es");
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
config.setLocale(locale);
am.updateConfiguration(config);
Locale locale = getResources().getConfiguration().locale;
String language = locale.getLanguage();
private WindowManager.LayoutParams mBarParams;
// init layout Params
mBarParams = new WindowManager.LayoutParams();
//重点添加此flag
mBarParams.flags = WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
//再对addView的View进行ontouch监听
case MotionEvent.ACTION_OUTSIDE:{
//做退出的具体逻辑处理
}
//解决方式1:返回不同的url。
//解决方式2:加上这2个设置。
Glide.with(this)
.diskCacheStrategy( DiskCacheStrategy.NONE )//禁用磁盘缓存
.skipMemoryCache( true )//跳过内存缓存
//直接调用的Runnable的run方法,并不是strat(),所以仅仅是当做一个有run()的普通类使用而已,并不是开启了一个新的线程。
private static void handleCallback(Message message) {
message.callback.run();
}
Activity与Fragment的生命周期,估计各位都很熟悉。但View的生命周期呢?见下图:
view.performClick()触摸该View点击事件。
activity.moveTaskToBack(true);按Back键回桌面,但不销毁该Activity。
ANR异常保存在/data/anr/traces.txt。
ViewPager切换动画。
//设置viewpager切换动画
ViewPager.setPageTransformer(true,new MyTrans());
//实现viewPager的切换动画
class MyTrans implements ViewPager.PageTransformer{
/**
* 根据偏移百分比,计算每个View的偏移比
*
* @param page 当前Fragment的View
* @param position view的偏移百分比
*/
@Override
public void transformPage(View page, float position) {
//TODO do your animation
}
}
//判断viewpager是否能继续左滑。(即已到最右的一页,判断准则有无惯性滑动)---若有内容的滑动是有惯性滑动状态。(当然排除极端无聊的用户)
/**
* 1.有内容的滑动的状态值变化:
* 1--->2---->0
*
* 2.无内容的滑动的状态值变化:
* 1--->0
*
*/
boolean isLeftScroll;
@Override
public void onPageScrollStateChanged(int state) {
//根据state进行判断
switch (state){
//手指滑动---状态值:1
case ViewPager.SCROLL_STATE_DRAGGING:{
}break;
//停止状态----状态值:0
case ViewPager.SCROLL_STATE_IDLE:{
//如果是最后一页(继续左滑是没有惯性滑动的),且没有惯性滑动时,跳转其他界面
if(!isLeftScroll &&viewPager.getCurrentItem()==(viewPager.getAdapter().getCount()-1)){
Intent intent = new Intent(this,CityChoiceActivity.class);
startActivity(intent);
finish();
}
isLeftScroll =false;
}break;
//惯性滑动--状态值:2
case ViewPager.SCROLL_STATE_SETTLING:{
isLeftScroll=true;
}break;
}
}
int[] viewLocation = new int[2];
View.getLocationOnScreen(viewLocation);
//Only use once
private int viewHeight = 0;
private int[] viewLocation = new int[2];
view.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// getting view height & width & screen postion
viewHeight = view.getHeight();
view.getLocationOnScreen(viewLocation);
// removeOnGlobalLayoutListener
view.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
}
});
//Get in real time
onresume(){
//TODO addOnGlobalLayoutListener
....
}
onpause(){
//TODO removeOnGlobalLayoutListener
...
}
try {
//TODO
} catch (Exception e) {
String exceptionStr = Log.getStackTraceString(e);
}
<item name="android:windowEnableSplitTouch">false</item>
<item name="android:splitMotionEvents">false</item>
//1.架包的依赖方式为编译时使用。
//2.project的build.gradle增加
allprojects {
repositories {
google()
jcenter()
}
//add this
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
options.compilerArgs.add('-Xbootclasspath/p:app/libs/framework-full.jar') //写架包的绝对路径或相对路径
}
}
}
//3.app的build.gradle增加
defaultConfig {
...
multiDexEnabled true
}
//4.显示依旧报错,但编译可以通过。
Toast toast = Toast.makeText(IndexActivity.this, "这个是一个测试", Toast.LENGTH_SHORT);
// 修改Toast字体大小
LinearLayout linearLayout = (LinearLayout) toast.getView();
TextView messageTextView = (TextView) linearLayout.getChildAt(0);
messageTextView.setTextSize(25);
// 显示Toast
toast.show();
//最简单打包后应用名称(还可以根据debug release等各版本设置对应apk名字)
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "customName.apk"
}
}
public static Bitmap screenShot(int width, int height) {
Class<?> demo = null;
Bitmap bitmap = null;
try {
demo = Class.forName("android.view.SurfaceControl");
} catch (Exception e) {
e.printStackTrace();
}
try {
Method method = demo.getMethod("screenshot", int.class, int.class);
Object obj = method.invoke(null, width, height);
bitmap = (Bitmap) obj;
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
<!--Button点击事件只是对TextView.setVisibility-->
<!--开启布局默认动画-->
<LinearLayout
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="200dp">
<TextView
android:text="666"
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="with Animation"
android:onClick="one"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<TextView
android:text="666"
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="none Animation"
android:onClick="two"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Drawable drawable = drawable = getResources().getDrawable(R.drawable.wifi_active);
//important
drawable.setBounds(0,0,drawable.getMinimumWidth(),drawable.getMinimumHeight());
//该方法源码已说明The Drawables must already have had {@link Drawable#setBounds} called.
tvWiFi.setCompoundDrawables(null,drawable,null,null);
//method 1:
setprop fw.max_users 8
setprop fw.show_multiuserui true
//method 2:
//配置多用户:
// /frameworks/base/core/res/res/values/config.xml
//修改其中的config_multiuserMaximumUsers的值为4 (看要支持几个用户)
//修改之后,设置中能看到用户这个菜单,而不是本来就有的账户。
//方法一:
public Bitmap createViewBitmap(View v) {
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
v.draw(canvas);
return bitmap;
}
//方法二:
/**
* 截取scrollview的屏幕
* @param scrollView
* @return
*/
public static Bitmap getBitmapByView(ScrollView scrollView) {
int h = 0;
Bitmap bitmap = null;
// 获取scrollview实际高度
for (int i = 0; i < scrollView.getChildCount(); i++) {
h += scrollView.getChildAt(i).getHeight();
scrollView.getChildAt(i).setBackgroundColor(
Color.parseColor("#ffffff"));
}
// 创建对应大小的bitmap
bitmap = Bitmap.createBitmap(scrollView.getWidth(), h,
Bitmap.Config.RGB_565);
final Canvas canvas = new Canvas(bitmap);
scrollView.draw(canvas);
return bitmap;
}
adb shell am broadcast -a com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD --ei 'seq' 1
//seq 具体值看实际情况通常是1
public void setDeviceName(String devName) {
WifiP2pManager manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
WifiP2pManager.Channel channel = manager.initialize(this, getMainLooper(), null);
try {
Class[] paramTypes = new Class[3];
paramTypes[0] = WifiP2pManager.Channel.class;
paramTypes[1] = String.class;
paramTypes[2] = WifiP2pManager.ActionListener.class;
Method setDeviceName = manager.getClass().getMethod(
"setDeviceName", paramTypes);
setDeviceName.setAccessible(true);
Object arglist[] = new Object[3];
arglist[0] = channel;
arglist[1] = devName;
arglist[2] = new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
}
@Override
public void onFailure(int reason) {
}
};
setDeviceName.invoke(manager, arglist);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
例如:
values-zh-rCN
values-zh-rHK
请补上
values-zh
(
why?
避免有些是设置values-zh-rTW,若没有对应相应适配。会优先匹配values-zh,再没有才匹配values。
)
//1.建color文件夹 res/color/
//2.建color-selector文件,color_test_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@color/system_color" />
<item android:state_focused="true" android:color="@color/system_color" />
<item android:color="@color/color_white_light" />
</selector>
//3.应用
//3.1布局中的应用
android:textColor="@color/color_test_selector"
//3.2代码中的应用
textView.setTextColor(getResources().getColorStateList(R.color.color_test_selector));
//method1:
TextView tv = new TextView(this);
int id = View.generateViewId();//api>=17
tv.setId(id);
//method2:
//2.1在values中创建ids.xml文件
//2.2文件内容:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="circle_centerview" type="id" />
</resources>
//2.3代码中使用
TextView tv = new TextView(this);
tv.setId(R.id.circle_centerview);
//method1 还包含内部存储路径(需自行过滤)
private String[] getStoragePaths(Context context) {
StorageManager storageManager = (StorageManager) context
.getSystemService(Context.STORAGE_SERVICE);
List<String> pathsList = new ArrayList<String>();
try {
Class<?>[] paramClasses = {};
Method getVolumeList = StorageManager.class.getMethod("getVolumeList", paramClasses);
Object[] params = {};
Object[] invokes = (Object[]) getVolumeList.invoke(storageManager, params);
if (invokes != null) {
for (int i = 0; i < invokes.length; i++) {
Object obj = invokes[i];
Method getPath = obj.getClass().getMethod("getPath", new Class[0]);
String path = (String) getPath.invoke(obj, new Object[0]);
pathsList.add(path);
//realmo add test usb label
Method getUserLabel = obj.getClass().getMethod("getUserLabel",new Class[0]);
String lable = (String) getUserLabel.invoke(obj,new Object[0]);
Log.d("realmo","label:"+lable);
Log.d("realmo","path:"+path);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return pathsList.toArray(new String[pathsList.size()]);
}
//method2
private String[] getStoragePaths(Context context) {
List<String> pathsList = new ArrayList<String>();
StorageManager storageManager = (StorageManager) cxt
.getSystemService(Context.STORAGE_SERVICE);
try {
Method method = StorageManager.class
.getDeclaredMethod("getVolumePaths");
method.setAccessible(true);
Object result = method.invoke(storageManager);
if (result != null && result instanceof String[]) {
String[] pathes = (String[]) result;
StatFs statFs;
for (String path : pathes) {
if (!TextUtils.isEmpty(path) && new File(path).exists()) {
statFs = new StatFs(path);
if (statFs.getBlockCount() * statFs.getBlockSize() != 0) {
if (path.contains("/sdcard"))
continue;
pathsList.add(path);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
File externalFolder = Environment.getExternalStorageDirectory();
if (externalFolder != null) {
pathsList.add(externalFolder.getAbsolutePath());
}
}
return pathsList.toArray(new String[pathsList.size()]);
}
private String getForegroundActivity(){
ActivityManager am = (ActivityManager) mContext
.getSystemService(android.content.Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> lst = am.getRunningTasks(1);
return lst.get(0).topActivity.flattenToString();
}
Intent intent = new Intent(LOCK_SCREEN_ACTIVITY_NAME);
//判断是否存在
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}else{
}
<< : 左移运算符,num << 1,相当于num乘以2
>> : 右移运算符,num >> 1,相当于num除以2
//在Activity的onDestroy里方法里如下代码
@Override
protected void onDestroy() {
if (mWebView != null) {
try {
ViewGroup parent = (ViewGroup) mWebView.getParent();
if (parent != null) {
parent.removeView(mWebView);
}
mWebView.removeAllViews();
mWebView.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
super.onDestroy();
}
//task to delete the old jar
task deleteOldJar(type: Delete) {
delete 'release/your jar name.jar'
}
//task to export contents as jar
task exportJar(type: Copy) {
from('build/intermediates/packaged-classes/release/')
into('release/')
include('classes.jar')
///Rename the jar
rename('classes.jar', 'your jar name.jar')
}
private void killApp(String packageName) {
ActivityManager activityManager = (ActivityManager) this.
getSystemService(Context.ACTIVITY_SERVICE);
//获取系统中所有正在运行的进程
List<ActivityManager.RecentTaskInfo> recents = activityManager.getRecentTasks(Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
if (recents == null || recents.size() < 1) {
return;
}
try {
Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
Method mRemoveTask = activityManagerClass.getMethod("removeTask", new Class[]{int.class, int.class});
mRemoveTask.setAccessible(true);
// List<ActivityManager.RecentTaskInfo> recents = activityManager.getRecentTasks(Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
for (int i = 1; i < recents.size(); i++) {
if (recents.get(i).baseIntent.getComponent().flattenToShortString().contains(packageName)) {
mRemoveTask.invoke(activityManager, recents.get(i).persistentId, 0);
}
}
} catch (Exception e) {
e.printStackTrace();
}
// activityManager.removeTask(recents.get(i).persistentId,ActivityManager.REMOVE_TASK_KILL_PROCESS);
}
LocalePicker.updateLocale(locale);
//demo
Class<?> cls;
try {
cls = Class.forName("com.android.internal.inputmethod.InputMethodUtils$InputMethodSettings"); //重点用 &
Constructor<?> cons=null;
cons=cls.getConstructor(Resources.class, ContentResolver.class, HashMap.class, ArrayList.class, int.class, boolean.class);
mSettings = (InputMethodSettings) cons.newInstance(context.getResources(), context.getContentResolver(),
mMethodMap, mMethodList, getDefaultCurrentUserId(),true);//创建新对象时调用构造方法
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//method1:
try{
ComponentName componentName = new ComponentName(pkg, cls);
Intent mIntent = new Intent(Intent.ACTION_MAIN);
mIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mIntent.setComponent(componentName);
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(mIntent);
} catch(ActivityNotFoundException e){
//TODO other
}
//method2:
Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
/**
* 是否在充电
*/
public static boolean isPlugged(Context context){
//发送一个包含充电状态的广播,并用粘性广播
IntentFilter filter=new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent intent = context.registerReceiver(null, filter);
//获取充电状态
int isPlugged=intent.getIntExtra(BatteryManager.EXTRA_PLUGGED,-1);
boolean acPlugged=isPlugged==BatteryManager.BATTERY_PLUGGED_AC;
boolean usbPlugged=isPlugged==BatteryManager.BATTERY_PLUGGED_USB;
boolean wrieliessPlugged=isPlugged==BatteryManager.BATTERY_PLUGGED_WIRELESS;
return acPlugged || usbPlugged || wrieliessPlugged;
}
/**
* 抢占音乐焦点
*/
public static void stopSound(Context context){
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
am.requestAudioFocus(null,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
<style name="MyTranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
</style>
private void updateLanguage(Locale locale) {
try {
Object objIActMag, objActMagNative;
Class clzIActMag = Class.forName("android.app.IActivityManager");
Class clzActMagNative = Class.forName("android.app.ActivityManagerNative");
Method mtdActMagNative$getDefault = clzActMagNative.getDeclaredMethod("getDefault");
// IActivityManager iActMag = ActivityManagerNative.getDefault();
objIActMag = mtdActMagNative$getDefault.invoke(clzActMagNative);
// Configuration config = iActMag.getConfiguration();
Method mtdIActMag$getConfiguration = clzIActMag.getDeclaredMethod("getConfiguration");
Configuration config = (Configuration) mtdIActMag$getConfiguration.invoke(objIActMag);
config.locale = locale;
// iActMag.updateConfiguration(config);
// 此处需要声明权限:android.permission.CHANGE_CONFIGURATION
// 会重新调用 onCreate();
Class[] clzParams = { Configuration.class };
Method mtdIActMag$updateConfiguration = clzIActMag.getDeclaredMethod(
"updateConfiguration", clzParams);
mtdIActMag$updateConfiguration.invoke(objIActMag, config);
} catch (Exception e) {
e.printStackTrace();
}
}
%s表示字符串类型占位符,%d表示整型占位符,%f表示浮点型占位符。
实际使用的时候一般都会使用%n$s,这里的n表示索引,第几个要被替换的字符串,而且String.format这个方法也很给力,他可以计算出你的string.xml中有多少个占位符,就让你可以填充多少参数。
var isOpen: Boolean = false
set(value) {
//field 代表当前变量
field = value
if (value) ....}
//该方法没有生命周期限制,可以放在onresume可以放在oncreate中,会在CPU空闲的时候会调用
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
System.out.println("addIdleHandler");
// 主线程空闲执行此方法
// return true 会执行很多遍
return false;
}
});
方法一:系统自动扫描apk安装方式
frameworks\base\services\java\com\android\server\pm下
修改PackageManagerService下的createDataDirsLI()方法拦截要安装的PackageName是否允许安装。
方法二,adb install 命令安装方式
frameworks\base\cmds\pm\src\com\android\commands\pm下
修改Pm下的runInstall()方法,拦截要安装的packageName是否允许安装。
Copyright (c) 2018 RealMo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.