Android开发笔记
0.导入字串
- 合并key和value
=CONCATENATE("<string name=",CHAR(34), $A2,CHAR(34), ">", C2, "</string>")
- 分离key和value
举例:
在A3输入:<string name="app_name">HiShare</string>
提取key:
=MID(A3,FIND("name=""",A3)+6,FIND(""">",A3)-FIND("name=""",A3)-6)
提取value:
=MID(A3,FIND(""">",A3)+2,FIND("</",A3)-FIND(""">",A3)-2)
或者
=MID(A3,FIND(">",A3)+1,FIND("</",A3)-FIND(">",A3)-1)
A3表示在A3往下输入数据。
多国语言:
以下是根据给出的语言和地区对应的命名约定,为Danish、Finnish、Russian、German、Dutch、Norwegian、Swedish、Kazakh、Latvian、Lithuanian、Slovak、Slovenian、Estonian、Czech、Hungarian、Bulgarian、Croatian、Ukrainian、Romanian和Greek创建多国语言XML文件时的命名示例:
Euskera(巴斯克语):values-eu/strings.xml
这个文件夹是用于存储巴斯克语环境下的字符串资源文件。eu表示巴斯克语。
Gallego(加利西亚语):values-gl/strings.xml
这个文件夹是用于存储加利西亚语环境下的字符串资源文件。gl表示加利西亚语。
French(法语):values-fr/strings.xml
这个文件夹是用于存储法语环境下的字符串资源文件。fr表示法语。
Italian(意大利语):values-it/strings.xml
这个文件夹是用于存储意大利语环境下的字符串资源文件。it表示意大利语。
Polish(波兰语):values-pl/strings.xml
这个文件夹是用于存储波兰语环境下的字符串资源文件。pl表示波兰语。
Danish(丹麦语):values-da/strings.xml
这个文件夹是用于存储丹麦语环境下的字符串资源文件。da表示丹麦语。
Finnish(芬兰语):values-fi/strings.xml
这个文件夹是用于存储芬兰语环境下的字符串资源文件。fi表示芬兰语。
Russian(俄语):values-ru/strings.xml
这个文件夹是用于存储俄语环境下的字符串资源文件。ru表示俄语。
German(德语):values-de/strings.xml
这个文件夹是用于存储德语环境下的字符串资源文件。de表示德语。
Dutch(荷兰语):values-nl/strings.xml
这个文件夹是用于存储荷兰语环境下的字符串资源文件。nl表示荷兰语。
Norwegian(挪威语):values-no/strings.xml
这个文件夹是用于存储挪威语环境下的字符串资源文件。no表示挪威语。
Swedish(瑞典语):values-sv/strings.xml
这个文件夹是用于存储瑞典语环境下的字符串资源文件。sv表示瑞典语。
Kazakh(哈萨克语):values-kk/strings.xml
这个文件夹是用于存储哈萨克语环境下的字符串资源文件。kk表示哈萨克语。
Latvian(拉脱维亚语):values-lv/strings.xml
这个文件夹是用于存储拉脱维亚语环境下的字符串资源文件。lv表示拉脱维亚语。
Lithuanian(立陶宛语):values-lt/strings.xml
这个文件夹是用于存储立陶宛语环境下的字符串资源文件。lt表示立陶宛语。
Slovak(斯洛伐克语):values-sk/strings.xml
这个文件夹是用于存储斯洛伐克语环境下的字符串资源文件。sk表示斯洛伐克语。
Slovenian(斯洛文尼亚语):values-sl/strings.xml
这个文件夹是用于存储斯洛文尼亚语环境下的字符串资源文件。sl表示斯洛文尼亚语。
Estonian(爱沙尼亚语):values-et/strings.xml
这个文件夹是用于存储爱沙尼亚语环境下的字符串资源文件。et表示爱沙尼亚语。
Czech(捷克语):values-cs/strings.xml
这个文件夹是用于存储捷克语环境下的字符串资源文件。cs表示捷克语。
Hungarian(匈牙利语):values-hu/strings.xml
这个文件夹是用于存储匈牙利语环境下的字符串资源文件。hu表示匈牙利语。
Bulgarian(保加利亚语):values-bg/strings.xml
这个文件夹是用于存储保加利亚语环境下的字符串资源文件。bg表示保加利亚语。
Croatian(克罗地亚语):values-hr/strings.xml
这个文件夹是用于存储克罗地亚语环境下的字符串资源文件。hr表示克罗地亚语。
Ukrainian(乌克兰语):values-uk/strings.xml
这个文件夹是用于存储乌克兰语环境下的字符串资源文件。uk表示乌克兰语。
Romanian(罗马尼亚语):values-ro/strings.xml
这个文件夹是用于存储罗马尼亚语环境下的字符串资源文件。ro表示罗马尼亚语。
Greek(希腊语):values-el/strings.xml
这个文件夹是用于存储希腊语环境下的字符串资源文件。el表示希腊语。
Catalan-加泰罗尼亚语 :ca
设置系统语言
public boolean switchLanguage(String languageCode) {
Locale locale = null;
switch (languageCode){
case SIMPLIFY_CHINESE_LANGUAGE:
setLanguage(Locale.SIMPLIFIED_CHINESE); //zh-rCN 简体中文
break;
case TRADITIONAL_CHINESE_LANGUAGE:
setLanguage(Locale.TRADITIONAL_CHINESE); //zh-rTW 繁体中文
break;
case ENGLISH_LANGUAGE:
setLanguage(Locale.ENGLISH); //en 英语
break;
case GERMANY_LANGUAGE:
setLanguage(Locale.GERMANY); //de 德语
break;
case JAPANESE_LANGUAGE:
setLanguage(Locale.JAPANESE); //ja 日语
break;
case FRANCE_LANGUAGE:
setLanguage(Locale.FRANCE); //fr 法语
break;
case ITALIAN_LANGUAGE:
setLanguage(Locale.ITALIAN); //it 意大利语
break;
case KOREAN_LANGUAGE:
setLanguage(Locale.KOREAN); //ko 韩语
break;
case "nb":
locale = new Locale("nb"); //挪威语(Norwegian)
break;
case "nl":
locale = new Locale("nl"); //荷兰语(Dutch)
break;
case "pl":
locale = new Locale("pl"); //波兰语(Polish)
break;
case "pt":
locale = new Locale("pt"); //Portuguese 葡萄牙语
break;
case "ro":
locale = new Locale("ro"); //Romanian 罗马尼亚语
break;
case "ru":
locale = new Locale("ru"); //俄语(Russian)
break;
case "sv":
locale = new Locale("sv"); //瑞典语(Swedish)
break;
case "th":
locale = new Locale("th"); //泰语(Thai)
break;
case "tr":
locale = new Locale("tr"); //土耳其语(Turkish)
break;
case "vi":
locale = new Locale("vi"); //越南语(Vietnamese)
break;
case "hi":
locale = new Locale("hi"); //印地语(Hindi)
break;
case "hr":
locale = new Locale("hr"); //克罗地亚语(Croatian)
break;
case "hu":
locale = new Locale("hu"); //匈牙利语(Hungarian)
break;
case "in":
locale = new Locale("in"); //印尼语(Indonesian)
break;
case "el":
locale = new Locale("el"); //希腊语(Greek)
break;
case "es":
locale = new Locale("es"); //西班牙语(Spanish)
break;
case "fi":
locale = new Locale("fi"); //芬兰语(Finnish)
break;
case "ar":
locale = new Locale("ar"); //阿拉伯语(Arabic)
break;
case "bg":
locale = new Locale("bg"); //保加利亚语(Bulgarian)
break;
case "cs":
locale = new Locale("cs"); //捷克语(Czech)
break;
case "da":
locale = new Locale("da"); //丹麦语(Danish)
default:
setLanguage(Locale.CHINESE);
break;
}
if (locale != null) {
setLanguage(locale);
}
return true;
}
public void setLanguage(Locale mLocale) {
try {
Class localPicker = Class.forName("com.android.internal.app.LocalePicker");
Method updateLocale = localPicker.getDeclaredMethod("updateLocale",
Locale.class);
updateLocale.invoke(null,mLocale);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException
| IllegalAccessException e) {
e.printStackTrace();
}
}
1.HTML更换字体粗细
- 只换一个
TextView textView = findViewById(R.id.sign_up_link);
textView.setText(Html.fromHtml("<span style="font-weight:300">正在投屏</span>"));
//500为中度
‘font-weight’属性执行字体中字形的重量,这取决于黑度等级或笔划粗细。
其值的意义如下:
100至900
这些有序排列中的每个值,表示至少与其起身拥有相同黑度的重量。其大致符合下列通用重量名称:
100 - Thin
200 - Extra Light (Ultra Light)
300 - Light
400 - Normal
500 - Medium
600 - Semi Bold (Demi Bold)
700 - Bold
800 - Extra Bold (Ultra Bold)
900 - Black (Heavy)
String wireless="<font color='#7870df'><b>%s</b></font>";
String formatWireless=String.format(wireless,"\''Apps Manager\''");
@SuppressLint({"StringFormatInvalid", "LocalSuppress"})
String html=this.getString(R.string.tv_smart_wiress_tip_two,formatWireless);
html = html.replace("\n","<br />");
tvSmartOpenTipsTwo.setText(Html.fromHtml(html));
tvOk = view.findViewById(R.id.tv_ok);
tvOk.setOnClickListener(this);
-
换多个
private void initViewContact(View view) { tvSmartOpenTips = view.findViewById(R.id.tv_smart_open_tips); String wireless="<font color='#7972d2'><b>%s</b></font>"; String formatWireless1 = String.format(wireless,"ccps_prj@benq.com"); String formatWireless2 = String.format(wireless,"BenQ Smart Control feedback"); String formatWireless3 = String.format(wireless,"Your Problems…"); String html = this.getString(R.string.tv_contact_us_content,formatWireless1,formatWireless2,formatWireless3); html = html.replace("\n","<br />"); tvSmartOpenTips.setText(Html.fromHtml(html)); } <string name="tv_contact_us_content">To : %1$s\nSubject : %2$s\nMessage : %3$s</string>
左对齐 <TextView android:id="@+id/tv_smart_open_tips" android:layout_marginTop="30dp" android:textSize="16sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="20dp" android:paddingRight="20dp" android:textColor="#a4a4a4" android:textAlignment="textStart" android:lineSpacingExtra="10dp" android:gravity="start" />
2.缩小为原来的80%
iv_video_center.setScaleX(0.8f);
iv_video_center.setScaleY(0.8f);
3.音乐进度条
<SeekBar
android:id="@+id/sb_player"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginRight="@dimen/size_30dp"
android:layout_toRightOf="@+id/tv_player_progress"
android:indeterminate="false"
android:maxHeight="6dp"
android:progressDrawable="@drawable/sb_player_progress"
android:thumb="@drawable/sb_player_thumb" />
sb_player_progress.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<solid android:color="@color/c_EBEBEB"/>
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="#FF444444"/>
</shape>
</clip>
</item>
</layer-list>
sb_player_thumb.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<solid android:color="#FF444444"/>
<size
android:height="25dp"
android:width="25dp" />
</shape>
4.屏幕分辨率问题
1. 60*60 ==== xhdp
常见手机尺寸和分辨率
以下是一些常见手机品牌(苹果、vivo、oppo和三星)中几个型号的尺寸、分辨率和屏幕密度:
**苹果(Apple)**
- iPhone 12 Pro Max:
- 尺寸:6.7 英寸
- 分辨率:2778 x 1284 像素
- 密度:458 dpi
- iPhone 12 Pro:
- 尺寸:6.1 英寸
- 分辨率:2532 x 1170 像素
- 密度:460 dpi
**vivo**
- vivo X60 Pro+:
- 尺寸:6.56 英寸
- 分辨率:2376 x 1080 像素
- 密度:398 dpi
- vivo Y72 5G:
- 尺寸:6.58 英寸
- 分辨率:2408 x 1080 像素
- 密度:401 dpi
**oppo**
- OPPO Find X3 Pro:
- 尺寸:6.7 英寸
- 分辨率:3216 x 1440 像素
- 密度:525 dpi
- OPPO Reno5 Pro+ 5G:
- 尺寸:6.55 英寸
- 分辨率:2400 x 1080 像素
- 密度:402 dpi
**三星(Samsung)**
- Samsung Galaxy S21 Ultra:
- 尺寸:6.8 英寸
- 分辨率:3200 x 1440 像素
- 密度:515 dpi
- Samsung Galaxy A52:
- 尺寸:6.5 英寸
- 分辨率:2400 x 1080 像素
- 密度:407 dpi
请注意,这些数据仅为常见型号的示例。不同的手机型号和系列可能会有不同的尺寸、分辨率和屏幕密度。在开发过程中,建议您参考特定设备的规格表或使用模拟器进行测试,以确保应用程序在各种设备上都能正常显示和运行。
5.轮播图小圆点案例
要根据轮播图数量设置小圆点并监听ViewPager的滑动,以及根据当前页面设置小圆点的颜色,您可以按照以下步骤进行操作:
-
在布局文件中添加小圆点:
在您的布局文件中,添加一个水平方向的LinearLayout
作为小圆点容器,并在其中动态添加小圆点。例如:<LinearLayout android:id="@+id/dotsContainer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="center"> </LinearLayout>
-
在Activity中设置ViewPager和小圆点:
-
首先,在Activity中获取对应的ViewPager和LinearLayout:
ViewPager viewPager = findViewById(R.id.viewPager); LinearLayout dotsContainer = findViewById(R.id.dotsContainer);
-
然后,根据轮播图数量动态创建小圆点:
private void createDots(int pageCount) { for (int i = 0; i < pageCount; i++) { ImageView dot = new ImageView(this); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ); params.setMargins(8, 0, 8, 0); dot.setLayoutParams(params); dot.setImageResource(R.drawable.dot_gray); // 设置初始状态为灰色 dotsContainer.addView(dot); } }
在上述代码中,我们根据轮播图数量使用循环动态创建小圆点,并设置初始状态为灰色,然后将其添加到LinearLayout中。
-
-
监听ViewPager的滑动事件:
-
首先,在Activity中实现
ViewPager.OnPageChangeListener
接口:public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener { // ... }
-
然后,将当前Activity作为ViewPager的滑动监听器:
viewPager.addOnPageChangeListener(this);
-
最后,实现以下三个回调方法:
@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // 不需要实现该方法 } @Override public void onPageSelected(int position) { // 根据当前页面设置小圆点颜色 for (int i = 0; i < dotsContainer.getChildCount(); i++) { ImageView dot = (ImageView) dotsContainer.getChildAt(i); dot.setImageResource(i == position ? R.drawable.dot_black : R.drawable.dot_gray); } } @Override public void onPageScrollStateChanged(int state) { // 不需要实现该方法 }
在
onPageSelected()
方法中,我们根据当前选中的页面位置来设置相应小圆点的颜色。如果页面位置与小圆点的索引相等,则设置为黑色,否则设置为灰色。
-
确保将上述代码适配到您的具体布局和资源文件中,并在Activity的适
6.下拉刷新案例
在Android应用中,下拉刷新功能通常用于在用户下拉屏幕时更新数据或重新加载内容。这种功能可以通过使用SwipeRefreshLayout
布局来实现。以下是一个简单的示例:
- 在布局文件中添加
SwipeRefreshLayout
作为根布局,并在其中包裹要刷新的内容。例如:
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 添加你的内容视图 -->
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
- 在相应的Activity或Fragment中,找到
SwipeRefreshLayout
并设置刷新操作的监听器。例如:
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
// 执行刷新操作,例如重新加载数据
loadData();
// 刷新完成后调用setRefreshing(false)停止刷新动画
swipeRefreshLayout.setRefreshing(false);
}
});
在上面的代码中,我们设置了一个OnRefreshListener
来监听下拉刷新操作。当用户下拉刷新时,onRefresh()
方法将被调用。在该方法内,可以执行刷新操作,例如重新加载数据或更新内容。
- 当刷新操作完成后,调用
setRefreshing(false)
来停止刷新动画。这通常在数据加载完成后被调用。
这样,您就可以在下拉屏幕时触发刷新操作,并在完成后停止刷新动画。根据您的具体需求,您可以在onRefresh()
方法中执行适当的处理,例如加载数据、更新UI等。
请注意,在使用SwipeRefreshLayout
时,确保只有一个直接子视图,并且它会填满整个布局。此外,还可以通过设置颜色方案和其他属性来自定义刷新样式。
7.shape属性
7.1shape根部
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape=["rectangle" | "oval" | "line" | "ring"] //共有4种类型,矩形(默认)/椭圆形/直线形/环形
>
//属性设置
</shape>
7.2属性
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape=["rectangle" | "oval" | "line" | "ring"] //共有4种类型,矩形(默认)/椭圆形/直线形/环形
// 以下4个属性只有当类型为环形时才有效
android:innerRadius="dimension" //内环半径
android:innerRadiusRatio="float" //内环半径相对于环的宽度的比例,比如环的宽度为50,比例为2.5,那么内环半径为20
android:thickness="dimension" //环的厚度
android:thicknessRatio="float" //环的厚度相对于环的宽度的比例
android:useLevel="boolean"> //如果当做是LevelListDrawable使用时值为true,否则为false.
<corners //定义圆角
android:radius="dimension" //全部的圆角半径与下面四个属性关系只能二选一
android:topLeftRadius="dimension" //左上角的圆角半径
android:topRightRadius="dimension" //右上角的圆角半径
android:bottomLeftRadius="dimension" //左下角的圆角半径
android:bottomRightRadius="dimension" /> //右下角的圆角半径
<gradient //定义渐变效果
android:type=["linear" | "radial" | "sweep"] //共有3中渐变类型,线性渐变(默认)/放射渐变/扫描式渐变
android:angle="integer" //渐变角度,必须为45的倍数,0为从左到右,90为从上到下
android:centerX="float" //渐变中心X的相当位置,范围为0~1
android:centerY="float" //渐变中心Y的相当位置,范围为0~1
android:startColor="color" //渐变开始点的颜色
android:centerColor="color" //渐变中间点的颜色,在开始与结束点之间
android:endColor="color" //渐变结束点的颜色
android:gradientRadius="float" //渐变的半径,只有当渐变类型为radial时才能使用
android:useLevel=["true" | "false"] /> //使用LevelListDrawable时就要设置为true。设为false时才有渐变效果
<padding //内部边距
android:left="dimension"
android:top="dimension"
android:right="dimension"
android:bottom="dimension" />
<size //自定义的图形大小 无size节点表示长宽自适应
android:width="dimension"
android:height="dimension" />
<solid //内部填充颜色
android:color="color" />
<stroke //描边
android:width="dimension" //描边的宽度
android:color="color" //描边的颜色
// 以下两个属性设置虚线 当dashGap和dashWidth有一个值为0,则描边为实线
android:dashWidth="dimension" //虚线的宽度,值为0时是实线
android:dashGap="dimension" /> //虚线的间隔
</shape>
8.渐变色背景
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#5c1968"
android:endColor="#1a7eba"
android:angle="180" />
</shape>
9.RecyclerView的item项设置边距
要为RecyclerView的item项设置边距,您可以通过自定义ItemDecoration来实现。ItemDecoration是RecyclerView提供的一个用于在item之间绘制装饰或分隔线的类。
以下是一种常用的方法来设置RecyclerView的item项边距:
- 创建一个类继承自RecyclerView.ItemDecoration,并重写
getItemOffsets()
方法,该方法用于指定item的边距。
public class ItemSpacingDecoration extends RecyclerView.ItemDecoration {
private int spacing;
public ItemSpacingDecoration(int spacing) {
this.spacing = spacing;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.left = spacing;
outRect.right = spacing;
outRect.top = spacing;
outRect.bottom = spacing;
}
}
- 在您的Activity或Fragment中,将ItemDecoration应用到RecyclerView上。例如,在设置RecyclerView的布局管理器之后添加以下代码:
int spacingInPixels = getResources().getDimensionPixelSize(R.dimen.item_spacing);
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.addItemDecoration(new ItemSpacingDecoration(spacingInPixels));
在上面的代码中,我们创建了一个ItemSpacingDecoration对象,并传入所需的边距大小作为参数(以像素为单位)。然后,使用addItemDecoration()
方法将ItemDecoration应用到RecyclerView上。
- 最后,在您的dimens.xml文件中定义边距的大小。例如,在res/values/dimens.xml中添加以下行:
<dimen name="item_spacing">8dp</dimen>
这样,RecyclerView的每个item项都会有指定的边距,根据您在ItemSpacingDecoration中设置的值。您可以根据需要调整边距大小,并根据不同的item布局进行自定义。
请注意,通过使用ItemDecoration设置的边距适用于所有的item项。如果您希望为特定的item项设置不同的边距,可以在RecyclerView的Adapter中根据position来应用不同的边距。
10.RecyclerView设置数据adapter
要设置RecyclerView的数据适配器(Adapter),您需要执行以下步骤:
- 创建一个类继承自RecyclerView.Adapter,并指定泛型参数为您自定义的ViewHolder类型。例如,如果您有一个名为"CustomViewHolder"的ViewHolder类,可以这样定义适配器:
public class MyAdapter extends RecyclerView.Adapter<CustomViewHolder> {
// 在此处定义适配器的数据集合和其他必要的变量
@NonNull
@Override
public CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 创建并返回ViewHolder对象
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
return new CustomViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull CustomViewHolder holder, int position) {
// 填充ViewHolder的视图内容
// 使用数据集合中的数据来更新视图
}
@Override
public int getItemCount() {
// 返回数据集合的大小
return 0;
}
}
在上面的代码中,我们创建了一个适配器MyAdapter
,并重写了一些关键方法,包括onCreateViewHolder()
、onBindViewHolder()
和getItemCount()
。
-
在
onCreateViewHolder()
方法中,您需要创建并返回一个自定义的ViewHolder对象。通常,您会根据需要展示的每个item的布局文件,使用LayoutInflater从布局资源中创建视图。 -
在
onBindViewHolder()
方法中,您将填充ViewHolder的视图内容。根据提供的位置(position)和数据集合中的数据,更新视图的文本、图像或其他视图元素。 -
在
getItemCount()
方法中,返回数据集合的大小,指示RecyclerView中应该展示多少项。 -
在您的Activity或Fragment中,找到RecyclerView并设置适配器。例如:
RecyclerView recyclerView = findViewById(R.id.recyclerView);
MyAdapter adapter = new MyAdapter();
// 设置LinearLayoutManager
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
在上面的代码中,我们创建了一个适配器对象,并将其设置为RecyclerView的适配器。
请注意,以上代码是一个基本的框架示例,您需要根据自己的需求和数据结构进行适当的修改。确保在适配器中实现数据集合的管理和更新,以便正确地显示和更新RecyclerView的数据。
11.RecycleView设置item下划线
要在RecyclerView的列表项之间添加下划线,您可以使用以下方法:
- 创建一个XML文件用于定义下划线视图(divider)。
在您的res/drawable
目录下创建一个名为divider.xml
的XML文件,并添加以下代码:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size android:height="1dp" />
<solid android:color="@color/divider_color" />
</shape>
在上面的示例中,我们使用shape
标签定义了一个高度为1dp、颜色为@color/divider_color
的矩形形状。
- 在适配器的
onBindViewHolder
方法中应用下划线。
在适配器的onBindViewHolder
方法中,根据需要为列表项设置下划线。将下划线作为分割线背景应用到列表项的布局视图上。例如:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// ...
// 应用下划线
if (position < data.size() - 1) {
Drawable divider = ContextCompat.getDrawable(holder.itemView.getContext(), R.drawable.divider);
holder.itemView.setBackground(divider);
} else {
// 最后一项不显示下划线
holder.itemView.setBackground(null);
}
}
在上面的示例中,我们首先通过ContextCompat.getDrawable()
方法获取之前创建的下划线视图(divider.xml),然后将其设置为列表项的背景。最后,对于最后一项,我们将背景设为null以避免显示下划线。
请确保将R.drawable.divider
替换为您的divider.xml文件的资源ID,并根据需要调整条件来确定应用下划线的位置。
这样,当RecyclerView中有多个列表项时,它们之间就会通过下划线进行分隔。
12.修改顶部状态栏颜色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//5.0 全透明实现
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//4.4 全透明状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
im_help_back.setPadding(0, statusBarHeight, 0, 0);//子视图
tv_help_question_title.setPadding(0, statusBarHeight, 0, 0);//子视图
bg_devices_list_connect.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#5c1968"
android:endColor="#1a7eba"
android:angle="180" />
</shape>
13.监听键盘返回按钮
要实现键盘返回按钮的监听,您可以在活动(Activity)或片段(Fragment)的代码中进行处理。以下是一个示例:
在活动中:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// 在这里处理键盘返回按钮的逻辑
return true; // 返回true表示已经处理了该事件
}
return super.onKeyDown(keyCode, event);
}
在片段中:
@Override
public boolean onBackPressed() {
// 在这里处理键盘返回按钮的逻辑
return true; // 返回true表示已经处理了该事件
}
请注意,在片段中处理键盘返回按钮时,需要确保相关活动调用片段的onBackPressed()
方法。例如,在与片段关联的活动的onBackPressed()
方法中,调用当前片段的onBackPressed()
方法。
处理键盘返回按钮的逻辑可以根据您的需求进行自定义,例如执行特定操作、返回上一个界面或关闭当前活动等。
ExifInterface exif = null;
try {
exif = new ExifInterface(item.getFile().getAbsolutePath());
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
int rotationDegrees = 0;
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotationDegrees = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotationDegrees = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotationDegrees = 270;
break;
default:
rotationDegrees = 0;
break;
}
Matrix matrix = new Matrix();
matrix.postRotate(rotationDegrees);
Bitmap originalBitmap = BitmapFactory.decodeFile(item.getFile().getAbsolutePath());
Bitmap rotatedBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(), originalBitmap.getHeight(), matrix, true);
Glide.with(mContext).load(rotatedBitmap).override(Consts.THUMB_SIZE, Consts.THUMB_SIZE).placeholder(R.drawable.image_loading).error(R.drawable.image_loading).into(holder.ivIcon);
} catch (IOException e) {
throw new RuntimeException(e);
}
14.BitMap转File
要将Bitmap转换为File对象,您可以使用以下方法:
public File bitmapToFile(Bitmap bitmap, String filename) {
// 创建临时文件
File file = new File(context.getCacheDir(), filename);
try {
// 将Bitmap写入文件
FileOutputStream outputStream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.flush();
outputStream.close();
return file;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
上述代码将位图(Bitmap)对象写入一个临时文件中,并返回该文件。您需要提供一个文件名作为参数,以便命名临时文件。
请注意,上述代码假设您有一个上下文(Context)对象可用,它用于获取缓存目录(context.getCacheDir())。如果您不在Activity或Fragment中使用此代码,或者没有直接访问上下文对象的情况下,请相应调整获取缓存目录的方式。
以下是一个示例调用的代码:
Bitmap bitmap = ...; // 获取Bitmap对象
String filename = "example.png";
File file = bitmapToFile(bitmap, filename);
if (file != null) {
// 执行其他操作,例如上传文件或进行保存等
}
这样,您就可以将Bitmap转换为File对象,并对其进行进一步处理,如上传、保存等操作。
15.图片、文件Socket传送
要通过Socket发送图片或文件,您可以使用以下步骤:
- 创建一个Socket连接到服务器:
Socket socket = new Socket("server_ip", port);
- 获取与服务器之间的输出流,以便将数据发送给服务器:
OutputStream outputStream = socket.getOutputStream();
- 打开文件并读取其内容为字节数组:
File file = new File("path/to/your/file.jpg");
byte[] fileBytes = Files.readAllBytes(file.toPath());
- 将文件大小信息发送给服务器(可选):
int fileSize = fileBytes.length;
outputStream.writeInt(fileSize);
- 将文件内容发送给服务器:
outputStream.write(fileBytes);
outputStream.flush();
- 关闭输出流和Socket连接:
outputStream.close();
socket.close();
上述代码片段打开一个Socket连接到服务器,然后从指定路径的文件中读取内容为字节数组,并将其发送给服务器。如果需要,还可以在发送文件内容之前发送文件大小信息。
请注意,这只是一个示例代码片段,具体实现可能需要根据您的应用程序需求进行调整。同样地,接收方服务器端也需要相应的代码来处理接收到的文件内容,并对其进行保存或进一步处理。
16.图片自带角度旋转问题
private void openImages(int position) {
List<PhotoItem> mPhotoItems1 = mPhotoItems;
if (position >= 0 && position < mPhotoItems1.size()) {
final File file = rotateToCorrect(mPhotoItems1.get(position).getFile());
File last = null;
File next = null;
if (position > 0) {
last = rotateToCorrect(mPhotoItems1.get(position - 1).getFile());
}
if (position < mPhotoItems1.size() - 1) {
next = rotateToCorrect(mPhotoItems1.get(position + 1).getFile());
}
rotateDeg = 0;
openImages(file, last, next);
}
}
private File rotateToCorrect(File file) {
String fileName = file.getAbsolutePath();
Bitmap originalBitmap = BitmapFactory.decodeFile(fileName);
Bitmap rotatedBitmap = null;
if (originalBitmap != null) {
// 1. 获取图片角度,翻转
ExifInterface exif = null;
try {
exif = new ExifInterface(fileName);
} catch (IOException e) {
throw new RuntimeException(e);
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
int rotationDegrees = 0;
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotationDegrees = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotationDegrees = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotationDegrees = 270;
break;
default:
break;
}
// 优化:速度
if(rotationDegrees == 0){
return file;
}
Matrix matrix = new Matrix();
matrix.postRotate(rotationDegrees);
rotatedBitmap = Bitmap.createBitmap(originalBitmap, 0, 0,
originalBitmap.getWidth(), originalBitmap.getHeight(), matrix, true);
}
// 2. 返回一个File文件
return bitmapToFile(rotatedBitmap,fileName);
}
/**
* BitMap转File
* @param bitmap
* @param filename
* @return
*/
public File bitmapToFile(Bitmap bitmap, String filename) {
// 创建临时文件
File cacheDir = getCacheDir();
String cacheFileName = "rotated_image_" + System.currentTimeMillis() + filename.substring(filename.indexOf("."));
File file = new File(cacheDir,cacheFileName);
try {
// 将Bitmap写入文件
FileOutputStream outputStream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);//很耗时
outputStream.flush();
outputStream.close();
Log.d("LXP","The Rotated Image File has been created!");
return file;
} catch (Exception e) {
Log.d("LXP","Error saving rotated image!");
e.printStackTrace();
return null;
}
}
// 同时打开三张图片
private void openImages(final File file, final File last, final File next) {
mExecutorService.execute(new Runnable() {
@Override
public void run() {
mMediaManager.openImages(file, last, next);
}
});
}
17.多选按钮
要实现多选按钮,你可以使用 CheckBox
组件或自定义的选择框组件。下面是一个使用 CheckBox
组件来实现多选功能的示例:
- 在布局文件中添加
CheckBox
组件来表示多选按钮:
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项1" />
- 在代码中为
CheckBox
组件设置监听器,以便在选中状态发生变化时进行相应操作:
CheckBox checkBox = findViewById(R.id.checkbox);
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// 处理选中状态改变事件
if (isChecked) {
// 选中时的操作
} else {
// 取消选中时的操作
}
}
});
在这个示例中,我们通过调用 setOnCheckedChangeListener
方法为 CheckBox
组件添加监听器。在监听器中,可以根据 isChecked
参数的值来判断是否选中了多选按钮,并在选中状态发生变化时执行相应的操作。
请根据你的具体需求修改代码,并根据需要处理选中和取消选中时的操作。例如,你可以将多个 CheckBox
组件放在一个 ListView
或 RecyclerView
中,并在点击多选按钮时更新数据模型、界面或其他操作。
18.对话框
private void showCaptureDialog(final String pathName) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.capture_success);
AlertDialog dialog = builder.create();
dialog.getWindow().setBackgroundDrawableResource(R.drawable.dialog_rounded_corners);
View dialogView = View.inflate(this, R.layout.dialog_capture, null);
ImageView ivImage = dialogView.findViewById(R.id.iv_capture_image);
ivImage.setScaleType(ImageView.ScaleType.FIT_CENTER);
ivImage.setImageBitmap(CaptureUtil.getSizedBitmap(pathName, this));
builder.setView(dialogView);
builder.setNegativeButton(R.string.capture_share, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent target = new Intent(Intent.ACTION_SEND);
target.setType("image/*");
target.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
startActivity(Intent.createChooser(target, getString(R.string.capture_share_title)));
} catch (Exception e) {
e.printStackTrace();
}
}
});
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.show();
}
19.颜色透明度
百分比:0% HEX: 00
百分比:1% HEX: 30
百分比:2% HEX: 50
百分比:3% HEX: 80
百分比:4% HEX: A0
百分比:5% HEX: D0
百分比:6% HEX: F0
百分比:7% HEX: 12
百分比:8% HEX: 14
百分比:9% HEX: 17
百分比:10% HEX: 1A
百分比:11% HEX: 1C
百分比:12% HEX: 1F
百分比:13% HEX: 21
百分比:14% HEX: 24
百分比:15% HEX: 26
百分比:16% HEX: 29
百分比:17% HEX: 2B
百分比:18% HEX: 2E
百分比:19% HEX: 30
百分比:20% HEX: 33
百分比:21% HEX: 36
百分比:22% HEX: 38
百分比:23% HEX: 3B
百分比:24% HEX: 3D
百分比:25% HEX: 40
百分比:26% HEX: 42
百分比:27% HEX: 45
百分比:28% HEX: 47
百分比:29% HEX: 4A
百分比:30% HEX: 4D
百分比:31% HEX: 4F
百分比:32% HEX: 52
百分比:33% HEX: 54
百分比:34% HEX: 57
百分比:35% HEX: 59
百分比:36% HEX: 5C
百分比:37% HEX: 5E
百分比:38% HEX: 61
百分比:39% HEX: 63
百分比:40% HEX: 66
百分比:41% HEX: 69
百分比:42% HEX: 6B
百分比:43% HEX: 6E
百分比:44% HEX: 70
百分比:45% HEX: 73
百分比:46% HEX: 75
百分比:47% HEX: 78
百分比:48% HEX: 7A
百分比:49% HEX: 7D
百分比:50% HEX: 80
百分比:51% HEX: 82
百分比:52% HEX: 85
百分比:53% HEX: 87
百分比:54% HEX: 8A
百分比:55% HEX: 8C
百分比:56% HEX: 8F
百分比:57% HEX: 91
百分比:58% HEX: 94
百分比:59% HEX: 96
百分比:60% HEX: 99
百分比:61% HEX: 9C
百分比:62% HEX: 9E
百分比:63% HEX: A1
百分比:64% HEX: A3
百分比:65% HEX: A6
百分比:66% HEX: A8
百分比:67% HEX: AB
百分比:68% HEX: AD
百分比:69% HEX: B0
百分比:70% HEX: B3
百分比:71% HEX: B5
百分比:72% HEX: B8
百分比:73% HEX: BA
百分比:74% HEX: BD
百分比:75% HEX: BF
百分比:76% HEX: C2
百分比:77% HEX: C4
百分比:78% HEX: C7
百分比:79% HEX: C9
百分比:80% HEX: CC
百分比:81% HEX: CF
百分比:82% HEX: D1
百分比:83% HEX: D4
百分比:84% HEX: D6
百分比:85% HEX: D9
百分比:86% HEX: DB
百分比:87% HEX: DE
百分比:88% HEX: E0
百分比:89% HEX: E3
百分比:90% HEX: E6
百分比:91% HEX: E8
百分比:92% HEX: EB
百分比:93% HEX: ED
百分比:94% HEX: F0
百分比:95% HEX: F2
百分比:96% HEX: F5
百分比:97% HEX: F7
百分比:98% HEX: FA
百分比:99% HEX: FC
百分比:100% HEX: FF
20.页面加水印
package com.example.xhsdk.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class WatermarkView extends View {
private Paint paint;
private String watermarkText = "HiShare";
public WatermarkView(Context context,String text) {
super(context);
init();
if(text != null && !text.isEmpty())
watermarkText = text;
}
public WatermarkView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
paint = new Paint();
paint.setColor(Color.GRAY);
paint.setTextSize(50);
paint.setAlpha(30);
watermarkText = "Watermark";
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int viewWidth = getWidth();
int viewHeight = getHeight();
int textWidth = (int) paint.measureText(watermarkText);
// 计算倾斜角度(转换为弧度)
double tiltAngleRadians = Math.toRadians(25);
// 计算水平方向上的偏移量
float xOffset = (float) (Math.tan(tiltAngleRadians) * viewHeight);
for (int xPos = 0; xPos <= viewWidth + textWidth; xPos += textWidth + 100) {
for (int yPos = 0; yPos <= viewHeight; yPos += 250) {
canvas.save(); // 保存画布状态
canvas.rotate(-25, xPos + xOffset, yPos); // 设置旋转角度和旋转中心
canvas.drawText(watermarkText, xPos, yPos, paint);
canvas.restore(); // 恢复画布状态
}
}
}
}
private void setWatermark() {
WatermarkView watermarkView = new WatermarkView(this,APPConsts.getUsername());
rlMirrorRoot.addView(watermarkView);
}
21.页面跳转时渐变消失
Animation fadeoutAnimation = AnimationUtils.loadAnimation(FindDeviceActivity.this,R.anim.anim_alpha_out);
fadeoutAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
Intent intent = new Intent(FindDeviceActivity.this,WelcomeGuideActivity.class);
startActivity(intent);
NewRemoteMainActivity.isRestartMainActivity=false;
overridePendingTransition(0,0);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
srl_device_list.startAnimation(fadeoutAnimation);
22.adb命令
22.1在cmd中使用adb命令查看日志
1.查看所有日志:adb shell logcat 或者adb logcat
2.查看关键字日志:adb logcat -s SHY
3.adb logcat -v time > log.txt
这个命令会将日志输出到名为log.txt的文件中,-v time参数表示在日志中显示时间戳
22.2传输文件到设备
adb push app-release.apk /sdcard
adb push . /storage/emulated/0/JIT/MyAD/land/ //上传所有文件
22.3从设备拉取文件
adb pull 路径
22.4卸载系统应用
adb root
adb remount 切换root权限
adb shell
cd system/app
ls
rm -r HisenseBox_v* 卸载
ls
22.5查看package版本号
adb shell dumpsys package com.eshare.webserver|grep version
-
查看盒子信息:adb shell get prop
-
查看包:adb shell pm list packages
-
拉起某个应用:
adb shell am start -n <包名>/<主Activity> eg: am start -n com.eshare.jit/.Activity.PreambleActivity
-
卸载预装的版本
adb root adb remount adb shell pm path com.ecloud.eshare.server rm -rf EShareServer.apk sync reboot
-
查看固件版本:
adb shell dumpsys package 包名 |grep version
-
发送广播
adb shell am broadcast -a com.android.shuttle.service.SUSPEND
-
系统信息
1.查看系统信息:adb shell ----> getprop 2.查看cpu信息:cat /proc/cpuinfo 3.查看设备的序列号(即整机ID):adb shell dmctl get-serialno 4.查看内核日志:使用 dmesg 或查看 /var/log/messages 或 /var/log/syslog 来查看内核日志 5.查看系统某个信息: getprop persist.sys.orientation.value 设置系统某个信息:setprop persist.sys.orientation.value 0
-
查看aab的sdk
java -jar bundletool-all-x.x.x.jar dump manifest --bundle=your_app.aab
-
查看任务栈活动的activity
adb shell dumpsys activity activities | grep "com.example.myapp"
23.API33以后权限问题
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
public static boolean isAndroid33(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
return true;
}
return false;
}
24.按两次返回键退出应用
@Override
public void onBackPressed(){
long currentTime = System.currentTimeMillis();
if(currentTime - lastPressedTime < 2000){
super.onBackPressed();
}else{
lastPressedTime = currentTime;
Toast.makeText(this, R.string.backPressedTips,Toast.LENGTH_LONG).show();
}
}
25.自定义Toast
在Android中,默认情况下,系统的Toast是显示在屏幕的底部居中位置。系统提供的Toast类没有直接的方法来更改Toast的位置。
然而,你可以通过一些技巧来实现更改Toast的位置,如使用自定义布局和自定义样式。
以下是一种常见的方法来实现自定义Toast位置:
-
创建一个自定义布局文件(例如
custom_toast.xml
),并在其中定义Toast的布局和样式。例如,可以创建一个带有根布局为FrameLayout
的LinearLayout,并设置自己想要的位置。<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|center_horizontal" // 设置自定义位置 android:background="@drawable/toast_background" android:padding="16dp"> <!-- 自定义内容 --> <TextView android:id="@+id/toast_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:text="This is a custom toast" /> </FrameLayout>
-
在代码中使用自定义布局和样式来创建和显示Toast。
LayoutInflater inflater = getLayoutInflater(); View layout = inflater.inflate(R.layout.custom_toast, (ViewGroup) findViewById(R.id.custom_toast_container)); TextView text = layout.findViewById(R.id.toast_text); text.setText("This is a custom toast"); Toast toast = new Toast(getApplicationContext()); toast.setDuration(Toast.LENGTH_SHORT); toast.setView(layout); toast.show();
通过使用自定义布局和设置合适的位置,你可以更改Toast在屏幕上显示的位置。请根据你的需要进行调整,并确保选择的位置不会遮挡其他重要的UI元素。
需要注意的是,这种方法是使用自定义布局实现自定义位置的一种常见方式。但请记住,更改Toast的位置可能会违背Android设计准则,并且在某些设备和系统版本上可能不起作用或不稳定。因此,在决定更改Toast位置之前,建议仔细考虑用户体验和可用性的影响。
26.ICMP报文
Internet控制报文协议。
1. 它是TCP/IP协议簇的一个子协议,用于在IP主机、路由器之间传递控制消息。
2. 控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
27. AIDL进程通信
package com.example.aidlclientdemo;
import com.example.aidlService.CustomAIDL;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener {
private TextView mBindServiceTv;
private TextView mUnBindServiceTv;
private TextView mSayHelloTv;
private CustomAIDL mAidl;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
mAidl = CustomAIDL.Stub.asInterface(service);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
/**
* 初始化控件
*/
private void initView() {
mBindServiceTv = (TextView) findViewById(R.id.bind_service_tv);
mUnBindServiceTv = (TextView) findViewById(R.id.unbind_service_tv);
mSayHelloTv = (TextView) findViewById(R.id.send_msg);
setViewOnClickListener(mBindServiceTv, mUnBindServiceTv,mSayHelloTv);
setBtnFlags(true, false,false);
}
/**
* 为控件添加点击事件
*
* @param views
*/
private void setViewOnClickListener(View... views) {
for (View view : views) {
if (view != null) {
view.setOnClickListener(this);
}
}
}
/**
* 设置按钮的可点击状态
* @param bindService
* @param unbindService
*/
private void setBtnFlags(boolean bindService,boolean unbindService,boolean sayhello){
mBindServiceTv.setEnabled(bindService);
mUnBindServiceTv.setEnabled(unbindService);
mSayHelloTv.setEnabled(sayhello);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.unbind_service_tv:
//服务解绑
unbindService(conn);
setBtnFlags(true, false,false);
break;
case R.id.bind_service_tv:
//绑定服务
bindService(new Intent("action.aidlserver"), conn, BIND_AUTO_CREATE);
setBtnFlags(false, true,true);
break;
case R.id.send_msg:
//进程间通信
if (mAidl != null) {
try {
String str = mAidl.sayHello("hello,world");
Toast.makeText(getApplicationContext(), str,
Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
break;
default:
break;
}
}
}
28.系统功能命令
String HDMI_ONE_OUTPUT_DISABLE="echo off > /sys/class/drm/card0-HDMI-A-1/status";//关闭屏幕
String HDMI_ONE_OUTPUT_ENABLE="echo detect > /sys/class/drm/card0-HDMI-A-1/status";//开启屏幕
String SCREEN_SHOT = "screencap -p " + filePath + "\n"; //截屏
String SLEEP = "input keyevent KEYCODE_POWER";
String WAKEUP = "input keyevent KEYCODE_WAKEUP";
"reboot" //重启
"reboot -p" //关机
public static void execCmd(String cmd) {
Log.d(TAG, "execCmd " + cmd);
OutputStream outputStream = null;
DataOutputStream dataOutputStream = null;
try {
Process p = Runtime.getRuntime().exec("sh");
outputStream = p.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeBytes(cmd);
dataOutputStream.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != dataOutputStream) {
try {
dataOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (null != outputStream) {
try {
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private String getDeviceInfo(String s) {
try {
Class<?> clazz = Class.forName("android.os.SystemProperties");
Method method =clazz.getMethod("get",String.class);
String data = (String) method.invoke(clazz,s);
return data;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
Class<?> clazz = Class.forName("android.os.SystemProperties");
Method method = clazz.getMethod("set",String.class,Object.class);
hwrotation = (int) method.invoke(clazz,"sys.esharecap",enable);
29.切换系统语言
public void setLanguage(Locale mLocale) {
try {
Class localPicker = Class.forName("com.android.internal.app.LocalePicker");
Method updateLocale = localPicker.getDeclaredMethod("updateLocale",
Locale.class);
updateLocale.invoke(null,mLocale);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException
| IllegalAccessException e) {
e.printStackTrace();
}
}
setLanguage(Locale.ENGLISH);
30.请求权限
public static boolean requestCamera1Permissions(@NonNull Activity activity) {
List<String> permissions = new CopyOnWriteArrayList<>();
permissions.add(Manifest.permission.CAMERA);
for (String permission : permissions) {
int result = ActivityCompat.checkSelfPermission(activity.getApplicationContext(), permission);
if (result == PackageManager.PERMISSION_GRANTED) {
permissions.remove(permission);
}
}
if (!permissions.isEmpty()) {
String[] array = new String[permissions.size()];
permissions.toArray(array);
ActivityCompat.requestPermissions(activity, array, REQUEST_CAMERA_PERMISSIONS);
return false;
}
return true;
}
//多个权限
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PermissionUtils.REQUEST_SPEAKER_PERMISSIONS) {
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
startMirror();
}
}
}
//一个权限
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PermissionUtils.REQUEST_CAMERA_PERMISSIONS) {
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
toQRCaptureActivity();
}
}
}
public static boolean requestStorageManager(Activity activity,int requestCode){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
try {
Uri uri = Uri.parse("package:" + BuildConfig.APPLICATION_ID);
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, uri);
activity.startActivityForResult(intent,requestCode);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
return true;
}
31.Zxing扫码连接
依赖:
api 'com.google.zxing:core:3.3.3'
implementation 'com.king.zxing:zxing-lite:1.1.2-androidx'
1.xml文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.king.zxing.ViewfinderView
android:id="@+id/viewfinderView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:labelTextColor="#ffffff"
app:labelTextLocation="bottom"
app:laserColor="#e06c51"
app:frameColor="#e06c51"
app:cornerColor="#e06c51"
app:maskColor="#7f7f7f"
app:laserStyle="line"
app:gridHeight="0dp"/>
<RelativeLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#000000"
tools:ignore="MissingConstraints">
<ImageView
android:id="@+id/iv_scan_back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_gravity="center_vertical"
android:paddingRight="14dp"
android:src="@drawable/ic_back_dark_selector" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:ellipsize="middle"
android:gravity="center"
android:singleLine="true"
android:text="@string/qrcode_scan_title"
android:textColor="#ffffff"
android:textSize="18dp" />
</RelativeLayout>
<TextView
android:id="@+id/ivFlash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="560dp"
android:visibility="gone"
android:textColor="?android:attr/textColorPrimary"/>
</androidx.constraintlayout.widget.ConstraintLayout>
2.QRCapture.java
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.Rect;
import android.hardware.Camera;
import android.media.Image;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import com.eshare.businessclient.FindDeviceActivity;
import com.eshare.businessclient.QRCodeActivity;
import com.king.zxing.CaptureActivity;
import com.king.zxing.DecodeFormatManager;
import com.king.zxing.OnCaptureCallback;
import com.king.zxing.camera.CameraConfigurationUtils;
import com.king.zxing.camera.open.CameraFacing;
import com.king.zxing.camera.open.OpenCamera;
import com.king.zxing.camera.open.OpenCameraInterface;
import com.viewsonic.vcastsender.R;
import java.io.IOException;
public class QRCaptureActivity extends CaptureActivity implements SurfaceHolder.Callback, OnCaptureCallback {
private TextView ivFlash;
private RelativeLayout ll;
private ImageView ivScanBack;
private SurfaceView surfaceView;
int mSceenWidth;
int mSceenHeight;
public static void setMargins(View v, int l, int t, int r, int b) {
if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
p.setMargins(l, t, r, b);
v.requestLayout();
}
}
@Override
public int getLayoutId() {
return R.layout.activity_qrcapture;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("LXP","onCreate...");
ll = findViewById(R.id.ll);
setMargins(ll, 0, 50, 0, 0);
TextView tvTitle = findViewById(R.id.tvTitle);
tvTitle.setTextColor(Color.WHITE);
ivFlash = findViewById(R.id.ivFlash);
if (!hasTorch()) {
ivFlash.setVisibility(View.GONE);
}
ivScanBack = findViewById(R.id.iv_scan_back);
ivScanBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
// 2.设置相机预览
surfaceView = findViewById(R.id.surfaceView);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
//3.获取CaptureHelper,里面有扫码相关的配置设置
getCaptureHelper().playBeep(false)//播放音效
.vibrate(true)//震动
.decodeFormats(DecodeFormatManager.QR_CODE_FORMATS)//设置只识别二维码会提升速度
.supportVerticalCode(false)//支持扫垂直条码,建议有此需求时才使用。
.continuousScan(false);//是否连扫
}
/**
* 开启或关闭闪光灯(手电筒)
*
* @param on {@code true}表示开启,{@code false}表示关闭
*/
public void setTorch(boolean on) {
Camera camera = getCameraManager().getOpenCamera().getCamera();
Camera.Parameters parameters = camera.getParameters();
CameraConfigurationUtils.setTorch(parameters, on);
camera.setParameters(parameters);
}
/**
* 检测是否支持闪光灯(手电筒)
*
* @return
*/
public boolean hasTorch() {
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
}
/**
* 扫码结果回调
*
* @param result 扫码结果
* @return
*/
@Override
public boolean onResultCallback(String result) {
Log.d("LXP","onResultCallback ----- result:"+result);
//获取到扫码结果HmsScan,判断结果是否有效
if (result != null && !TextUtils.isEmpty(result)) {
//成功
Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putInt("result_type", 1);
bundle.putString("result_string", result);
resultIntent.putExtras(bundle);
setResult(RESULT_OK, resultIntent);
finish();
} else {
//失败
Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putInt("result_type", 2);
bundle.putString("result_string", "");
resultIntent.putExtras(bundle);
setResult(RESULT_OK, resultIntent);
finish();
}
return super.onResultCallback(result);
}
private void clickFlash(View v) {
boolean isSelected = v.isSelected();
setTorch(!isSelected);
v.setSelected(!isSelected);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.ivFlash:
clickFlash(v);
break;
}
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
}
}
3.activity
1.点击按钮扫码
case R.id.tv_pincode_scan:
if(PermissionUtils.requestCamera1Permissions(this)){
toQRCaptureActivity();
}
2.跳转扫码页面
private void toQRCaptureActivity() {
Log.d("LXP","scan qrcode");
Intent intent1 = new Intent();
intent1.putExtra(KEY_TITLE, this.getString(R.string.find_device));
intent1.setClass(this.getApplicationContext(), QRCaptureActivity.class);
startActivityForResult(intent1, REQUEST_CODE_SCAN);
}
3.数据回调
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
Log.d("LXP","onActivityResult-----requestCode:"+requestCode+" resultCode:"+resultCode);
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1 && resultCode == -1) {
String qrResult = data.getStringExtra("result_string");
Log.d("LXP","qrResult:"+qrResult);
if (!TextUtils.isEmpty(qrResult)) {
qrResult = FileUtils.toURLDecoder(qrResult);
parsingScanCodeData(qrResult);
}
}
}
32.安卓手机设置扩音开关
// 关闭/打开扬声器播放
public void setSpeakerStatus(boolean on) {
if (on) { //扬声器
mAudioManager.setSpeakerphoneOn(true);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
} else {
// 设置最大音量
int max = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, max, AudioManager.STREAM_VOICE_CALL);
// 设置成听筒模式
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
mAudioManager.setSpeakerphoneOn(false);// 关闭扬声器
mAudioManager.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL);
}
}
33.盒子连接网线的状态
当盒子插上网线后,黄灯可能表示以下几种情况:
黄灯闪动,绿灯长亮:这表示网线正常,正在通信中。
黄灯长亮,绿灯闪动:这可能是比较少见的情况,此时黄灯长亮可能是灯坏了,绿灯闪动可能是网线接触有问题,链路时断时好。
黄灯闪动,绿灯不亮:绿灯不亮表示线路可能存在问题,而黄灯闪动则可能表示正在尝试进行通信。
黄灯长亮,绿灯不亮:这通常表示网线短路了,可能是由于网线损坏或其他问题导致的。
黄灯不亮,绿灯闪动:这可能表示线路存在问题,如接触不良或网线内部断裂等。
黄灯不亮,绿灯长亮:这通常是正常情况,表示盒子已正确连接至网络但未进行通信。
34.开机自启动
1.注册广播接收器:
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
// 1. 启动Service
Intent intent1 = new Intent(context,ControlService.class);
context.startService(intent1);
// 2. 启动apk应用
// Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage("com.syxs.hicx");
// if (launchIntent != null) {
// launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// context.startActivity(launchIntent);
// }
}
}
}
2.在清单文件注册
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver android:name=".BootReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
35.MediaProjection
public Intent getMediaProjectionIntent(Context contexxt, String packageName,
boolean permanentGrant) throws RemoteException {
IBinder b = ServiceManager.getService (MEDIA_PROJECTION_SERVICE);
sMediaProjectionManager = IMediaProjectionManager. StubasInterface (b)
PackageManager packageManager = context.getPackageManaager ();
Application Info aInfo;
int uid;
try{
aInfo = packageManager.getApplicationInfo (packageName,0):
uid = aInfo.uid;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "unable to look up package name", e);
return null;
IMediaProjection projection = sMediaProjectionManager.createProjection (uid,
packageName,
MediaProjectionManager.TYPE_SCREEN_CAPTURE, permanentGrant);
Intent intent = new Intent():
intent. putExtra (MediaProjectionManager. EXTRA_MEDIA_PROJECTION,
projection. asBinder());
return intent;
}
36.跳转网络设置页
Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//在非activity里需添加
startActivity(intent);
37.系统功能
0.执行系统命令
Process process = Runtime.getRuntime().exec("screencap -p " + finalFilePath + "\n");
try {
// 等待命令执行完成
int exitCode = process.waitFor();
// 如果命令执行成功,返回1
if (exitCode == 0) {
return filePath;
} else {
// 如果命令执行失败,可以根据需要处理错误情况
System.err.println("Command execution failed with exit code: " + exitCode);
return "-1";
}
} catch (InterruptedException e) {
// 处理线程中断异常
e.printStackTrace();
return "-1";
}
1.静默卸载应用
private boolean uninstallApp(String packageName) {
Log.d("LXP","uninstall app ...");
installReceiver = new InstallReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED); // 添加您希望接收的广播动作
filter.addAction(Intent.ACTION_PACKAGE_ADDED); // 添加您希望接收的广播动作
filter.addAction(Intent.ACTION_DELETE); // 添加您希望接收的广播动作
filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); // 添加您希望接收的广播动作
context.registerReceiver(installReceiver, filter);
Intent broadcastIntent = new Intent();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
packageInstaller.uninstall(packageName, pendingIntent.getIntentSender());
broadcastIntent.putExtra("packageName",packageName);
broadcastIntent.setAction("android.intent.action.PACKAGE_FULLY_REMOVED");
sendBroadcast(broadcastIntent);
return true;
}
2.静默安装应用
private boolean installApp(String apkFilePath) {
Log.d("LXP","install app...");
//1.
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
try {
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
try (InputStream in = new FileInputStream(apkFilePath);
OutputStream out = session.openWrite("package", 0, -1)) {
byte[] buffer = new byte[8192];
int length;
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
session.fsync(out);
}
session.commit(PendingIntent.getBroadcast(context, sessionId,
new Intent(Intent.ACTION_MAIN), PendingIntent.FLAG_IMMUTABLE).getIntentSender());
String packageName = getPackageNameFromApk(context,apkFilePath);
params.setAppPackageName(packageName);
//安装完成发送广播
Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED);
intent.putExtra("packageName",packageName);
sendBroadcast(intent);
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
3.广播接收器
static class InstallReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("LXP","InstallReceiver onReceive,action:"+intent.getAction());
String packageName = intent.getStringExtra("packageName");
if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
Log.d("LXP","packageName:"+packageName);
try {
if (packageName != null) {
mControlObserver.onChanged(1078,packageName+":0");
Toast.makeText(context, "Package " + packageName + " installed", Toast.LENGTH_SHORT).show();
}else {
mControlObserver.onChanged(1078,packageName+":1");
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
} else if (intent.getAction().equals("android.intent.action.PACKAGE_FULLY_REMOVED")) {
try {
if (packageName != null) {
mControlObserver.onChanged(1079,packageName+":0");
Toast.makeText(context, "Package " + packageName + " removed", Toast.LENGTH_SHORT).show();
}else {
mControlObserver.onChanged(1079,packageName+":1");
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
}
public class CommonUtil {
public static final int REQUEST_IMAGE_PERMISSION = 0x111;
public static final int REQUEST_AUDIO_PERMISSION = 0x112;
public static final int REQUEST_VIDEO_PERMISSION = 0x112;
private static final int REQUEST_STARTSEARCHFILES = 7;
private static final String[] PERMISSION_STARTSEARCHFILES = new String[] {"android.permission.READ_EXTERNAL_STORAGE"};
private static final String[] PERMISSION_STARTSEARCHFILES33 = new String[] {Manifest.permission.READ_MEDIA_AUDIO,Manifest.permission.READ_MEDIA_IMAGES,Manifest.permission.READ_MEDIA_VIDEO};
public static boolean isAndroid33() {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
return true;
}
return false;
}
static void startSearchFilesWithPermissionCheck(@NonNull MainActivity target) {
if(isAndroid33()){
if (PermissionUtils.hasSelfPermissions(target, PERMISSION_STARTSEARCHFILES33)) {
target.startSearchFiles();
} else {
ActivityCompat.requestPermissions(target, PERMISSION_STARTSEARCHFILES33, REQUEST_STARTSEARCHFILES);
}
}else{
if (PermissionUtils.hasSelfPermissions(target, PERMISSION_STARTSEARCHFILES)) {
target.startSearchFiles();
} else {
ActivityCompat.requestPermissions(target, PERMISSION_STARTSEARCHFILES, REQUEST_STARTSEARCHFILES);
}
}
}
}
38.安卓存储路径问题
sdk大于30时:权限READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE默认返回false,因此需要注意这两个权限,相册拍照时,可以不申请这两个权限,当保存照片时,要取公共地址。
(1)手机sdk>30,需要获取这个外部地址:
public static final String PICS_PATH = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).getAbsolutePath();
(2)手机sdk<=30,可以访问这个地址:
private final String SD_PATH = Environment.getExternalStorageDirectory()
.getAbsolutePath();
@SuppressLint("SimpleDateFormat")
SimpleDateFormat format = new SimpleDateFormat(
"yyyyMMdd_HHmmss");
Date date = new Date(System.currentTimeMillis());
String time = format.format(date);
String path;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
path =PICS_PATH + "/mylive-" + time + ".jpg";
} else {
path = SD_PATH + "/eshare_image/mylive-" + time + ".jpg";
}
39.安卓分辨率适配
不同分辨率对应DPI 记录:
ldpi QVGA (240×320)
mdpi HVGA (320×480)
hdpi WVGA (480×800)
hdpi FWVGA (480×854)
xhdpi 720P(1280×720) --- HD
xxhdpi 1080p(1920×1080 ) --- FHD
xxxhdpi 4K(3840×2160) smallestWidth方法:2160/(dpi/160) = swXXXdp
**Android 性能调优系列**:`https://0a.fit/dNHYY`
**Android 车载学习指南**:`https://0a.fit/jdVoy`
**Android Framework核心知识点笔记**:`https://0a.fit/acnLL`
**Android 音视频学习笔记**:`https://0a.fit/BzPVh`
**Jetpack全家桶(含Compose)**:`https://0a.fit/GQJSl`
**Kotlin 入门到精进**:`https://0a.fit/kdfWR`
**Flutter 基础到进阶实战**:`https://0a.fit/xvcHV`
**Android 八大知识体系**:`https://0a.fit/mieWJ`
**Android 中高级面试题锦**:`https://0a.fit/YXwVq`
-
SmallestWidth适配
40.获取WIFI信息
package com.eshare.util;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
/**
* Created by StephenLee on 2017/11/9.
*/
public final class NetworkUtils {
private NetworkUtils() {
}
public static boolean isNetworkConnected(Context context) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);
NetworkInfo[] networkInfos = manager.getAllNetworkInfo();
if (networkInfos != null) {
for (NetworkInfo networkInfo : networkInfos) {
if (networkInfo != null && networkInfo.isConnected()) {
return true;
}
}
}
return false;
}
public static boolean isWifiConnected(Context context) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
return networkInfo != null && networkInfo.isConnected();
}
//获取当前wifi的IP
public static String getWifiIp(Context context) {
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo == null) {
return "";
}
String ipAddress = "";
int address = wifiInfo.getIpAddress();
byte[] bytes = { (byte) (0xFF & address),
(byte) (0xFF & (address >> 8)),
(byte) (0xFF & (address >> 16)),
(byte) (0xFF & (address >> 24)) };
try {
ipAddress = InetAddress.getByAddress(bytes).getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return ipAddress;
}
//获取wifi名称
public static String getWifiSsid(Context context) {
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo == null) {
return "";
}
return wifiInfo.getSSID().replace("\"", "");
}
public static boolean isWifiApEnabled(Context context) {
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
try {
Method method = WifiManager.class.getDeclaredMethod("isWifiApEnabled");
return (Boolean) method.invoke(wifiManager);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static String getWifiApSsid(Context context) {
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
try {
Method method = WifiManager.class.getDeclaredMethod("getWifiApConfiguration");
WifiConfiguration wifiConfiguration = (WifiConfiguration) method.invoke(wifiManager);
if (wifiConfiguration != null) {
return wifiConfiguration.SSID;
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
//获取本地局域网ip
public String getLocalIP(Context context) throws SocketException {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement();
Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
if (!address.isLoopbackAddress() && address.isSiteLocalAddress()) {
return address.getHostAddress();
}
}
}
return "";
}
}
//2023.12.18更新:添加音量调节
initVolume();
package com.ecloud.registration;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import android.annotation.TargetApi;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
import android.net.wifi.WifiManager;
import android.util.Log;
@TargetApi(9)
public class NetWorkHelper
{
private NetWorkHelper()
{
}
public static boolean isNetworkAvailable(Context context)
{
ConnectivityManager manager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = manager.getActiveNetworkInfo();
if (info != null && info.isConnectedOrConnecting())
{
return true;
}
return false;
}
public static boolean isWifiAvailable(Context context)
{
boolean result = false;
ConnectivityManager manager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
State wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
.getState();
if (wifi == State.CONNECTED)
{
result = true;
}
return result;
}
private static String hexByte(byte b) {
return String.format("%02X", b);
}
public static String geteth0MacAddress()
{
String macString = "00:00:00:00:00:00";
try {
NetworkInterface networkInterface = NetworkInterface.getByName("eth0");
if(networkInterface != null) {
byte[] mac = networkInterface.getHardwareAddress();
if(mac == null)
return macString;
StringBuilder builder = new StringBuilder();
for (byte b : mac) {
builder.append(hexByte(b));
builder.append(":");
}
builder.deleteCharAt(builder.length() - 1);
macString = builder.toString();
}
} catch (Exception e) {
e.printStackTrace();
Log.e("eshare","eshare 获取 eth0 Mac 地址时出错 --> "+e.getMessage(), e);
}
return macString;
}
public static String getwlan0MacAddress()
{
String macString = "00:00:00:00:00:00";
try {
NetworkInterface networkInterface = NetworkInterface.getByName("wlan0");
if(networkInterface != null) {
byte[] mac = networkInterface.getHardwareAddress();
if(mac == null)
return macString;
StringBuilder builder = new StringBuilder();
for (byte b : mac) {
builder.append(hexByte(b));
builder.append(":");
}
builder.deleteCharAt(builder.length() - 1);
macString = builder.toString();
}
} catch (Exception e) {
e.printStackTrace();
Log.e("eshare","eshare 获取 eth0 Mac 地址时出错 --> "+e.getMessage(), e);
}
return macString;
}
public static synchronized String[] getNetworkAddress(Context context)
{
String[] add = {"0.0.0.0","00:00:00:00:00:00"};
WifiManager mWifiManager = (WifiManager) context
.getSystemService(Context.WIFI_SERVICE);
int state = mWifiManager.getWifiApState();
if (state == WifiManager.WIFI_AP_STATE_ENABLED
|| state == WifiManager.WIFI_AP_STATE_ENABLING)
{
System.out.println("WIFI_AP_STATE_ENABLED");
try
{
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en
.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
Log.d("eshare","interface " + intf.getName());
if (intf.getName().contains("wlan")) {
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr
.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()
&& (inetAddress.getAddress().length == 4)) {
add[0] = inetAddress.getHostAddress();
add[1] = getMACAddress(inetAddress);
return add;
}
}
}
}
} catch (Exception e)
{
e.printStackTrace();
}
}
try
{
Enumeration<NetworkInterface> networkInterface = NetworkInterface
.getNetworkInterfaces();
while (networkInterface!=null&&networkInterface.hasMoreElements())
{
NetworkInterface ni = networkInterface.nextElement();
if (ni != null)
{
Enumeration<InetAddress> enumIpAddr = ni.getInetAddresses();
while (enumIpAddr.hasMoreElements())
{
InetAddress inetAddress = enumIpAddr.nextElement();// v6
String ip = inetAddress.getHostAddress();
int i = ip.indexOf(".");
if (inetAddress.isLoopbackAddress() || i == -1)
{
add[0] = "0.0.0.0";
add[1] = "00:00:00:00:00:00";
} else
{
add[0] = ip;
add[1] = getMACAddress(inetAddress);
return add;
}
}
}
}
} catch (Exception e)
{
e.printStackTrace();
}
return add;
}
// 获取MAC地址的方法
private static synchronized String getMACAddress(InetAddress ia) throws SocketException
{
// 获得网络接口对象(即网卡),并得到mac地址,mac地址存在于一个byte数组中。
byte[] mac = null;
try
{
mac = NetworkInterface.getByInetAddress(ia).getHardwareAddress();
}catch (Exception e) {
return "00:00:00:00:00:00";
}
// 下面代码是把mac地址拼装成String
StringBuffer sb = new StringBuffer();
for (int i = 0; i < mac.length; i++)
{
if (i != 0)
{
sb.append(":");
}
// mac[i] & 0xFF 是为了把byte转化为正整数
String s = Integer.toHexString(mac[i] & 0xFF);
sb.append(s.length() == 1 ? 0 + s : s);
}
// 把字符串所有小写字母改为大写成为正规的mac地址并返回
return sb.toString().toUpperCase();
}
}
40.1判断热点是否开启
Method method = wifiManager.getClass().getMethod("isWifiApEnabled");
method.setAccessible(true);
boolean isApEnabled = (Boolean) method.invoke(wifiManager);
if (isApEnabled) {
// 热点已开启
} else {
// 热点未开启
}
41.安卓MDNS局域网搜索设备
- 工具
package com.eshare.sender.tool;
import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import com.eshare.logger.Log;
import java.util.ArrayList;
import java.util.List;
public class FindMDNSDeviceTool {
private static final String TAG = "FindMDNSDeviceTool";
private final MDNSDeviceCallBack mdnsDeviceCallBack;
private NsdManager nsdManager;
private NsdManager.DiscoveryListener mDiscoverListener;
private List<NsdServiceInfo> allList;
private final String SERVICE_TYPE = "_http._tcp";
public interface MDNSDeviceCallBack{
void mdnsCallBack(List<NsdServiceInfo> list);
}
public FindMDNSDeviceTool(Context context,MDNSDeviceCallBack mdnsDeviceCallBack){
if(nsdManager != null){
nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
}
allList = new ArrayList<>();
this.mdnsDeviceCallBack = mdnsDeviceCallBack;
}
public void start(){
Log.d(TAG,"start to find mdns service");
allList.clear();
createDiscoverListener();
}
public void stop(){
Log.d(TAG,"stop to find mdns service");
nsdManager.stopServiceDiscovery(mDiscoverListener);
mDiscoverListener = null;
nsdManager = null;
allList.clear();
}
private void createDiscoverListener() {
if(mDiscoverListener != null){
mDiscoverListener = new NsdManager.DiscoveryListener() {
@Override
public void onStartDiscoveryFailed(String s, int i) {
android.util.Log.d(TAG,"onStartDiscoveryFailed...");
}
@Override
public void onStopDiscoveryFailed(String s, int i) {
android.util.Log.d(TAG,"onStopDiscoveryFailed...");
}
@Override
public void onDiscoveryStarted(String s) {
android.util.Log.d(TAG,"onDiscoveryStarted...");
}
@Override
public void onDiscoveryStopped(String s) {
android.util.Log.d(TAG,"onDiscoveryStopped...");
}
@Override
public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
android.util.Log.d(TAG,"onServiceFound...");
//这里只能获取到服务名称,要想获取ip地址和端口号需要使用NsdManager的resolveService()方法
nsdManager.resolveService(nsdServiceInfo, new NsdManager.ResolveListener() {
@Override
public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int i) {
android.util.Log.d(TAG,"onResolveFailed... i:"+i+" nsdServiceInfo:"+nsdServiceInfo);
}
@Override
public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
android.util.Log.d(TAG,"onServiceResolved... nsdServiceInfo:"+nsdServiceInfo);
boolean isHave = false;
for (int i = 0; i < allList.size(); i++) {
if(allList.get(i).getServiceName().equals(nsdServiceInfo.getServiceName())){
isHave = true;
break;
}
}
if(!isHave){
allList.add(nsdServiceInfo);
}
mdnsDeviceCallBack.mdnsCallBack(allList);
}
});
}
@Override
public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
android.util.Log.d(TAG,"onServiceLost...");
}
};
}
nsdManager.discoverServices(SERVICE_TYPE,NsdManager.PROTOCOL_DNS_SD,mDiscoverListener);
}
}
- 使用
new FindMDNSDeviceTool(this, new FindMDNSDeviceTool.MDNSDeviceCallBack() {
@Override
public void mdnsCallBack(List<NsdServiceInfo> list) {
Log.d("LXP","mdnsCllBack... list:"+list);
}
}).start();
42.获取网关IP和DNS
import android.content.Context;
import android.net.DhcpInfo;
import android.net.wifi.WifiManager;
import android.text.format.Formatter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.InetAddress;
import java.util.LinkedList;
public class PingUtil {
/**
* 获取网关(API>=29)
*/
public static String getGateway(Context context) {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
int gateway = dhcpInfo.gateway;
return Formatter.formatIpAddress(gateway);
}
/**
* 获取dns
*/
public static String getDns() {
String[] dnsServers = getDnsFromCommand();
// 组装
StringBuffer sb = new StringBuffer();
if (dnsServers != null) {
sb.append(dnsServers[0]); //注意这里只会返回1个dns服务器ip
}
return sb.toString();
}
//通过 getprop 命令获取
private static String[] getDnsFromCommand() {
LinkedList<String> dnsServers = new LinkedList<>();
try {
Process process = Runtime.getRuntime().exec("getprop");
InputStream inputStream = process.getInputStream();
LineNumberReader lnr = new LineNumberReader(new InputStreamReader(inputStream));
String line = null;
while ((line = lnr.readLine()) != null) {
int split = line.indexOf("]: [");
if (split == -1) continue;
String property = line.substring(1, split);
String value = line.substring(split + 4, line.length() - 1);
if (property.endsWith(".dns")
|| property.endsWith(".dns1")
|| property.endsWith(".dns2")
|| property.endsWith(".dns3")
|| property.endsWith(".dns4")) {
InetAddress ip = InetAddress.getByName(value);
if (ip == null) continue;
value = ip.getHostAddress();
if (value == null) continue;
if (value.length() == 0) continue;
dnsServers.add(value);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return dnsServers.isEmpty() ? new String[0] : dnsServers.toArray(new String[dnsServers.size()]);
}
}
43.自定义Dialog
Custom_dialog.java
package com.ecloud.utils;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.ecloud.eshare.MainActivity;
import com.ushare.client.R;
public class SelfDialog extends Dialog {
private Button yes;//确定按钮
private Button no;//取消按钮
private TextView titleTv;//消息标题文本
private TextView messageTv;//消息提示文本
private String titleStr;//从外界设置的title文本
private String messageStr;//从外界设置的消息文本
//确定文本和取消文本的显示内容
private String yesStr, noStr;
private onNoOnclickListener noOnclickListener;//取消按钮被点击了的监听器
private onYesOnclickListener yesOnclickListener;//确定按钮被点击了的监听器
public SelfDialog(Context context,String msg) {
super(context, R.style.MyDialog);
this.messageStr = msg;
}
/**
* 设置取消按钮的显示内容和监听
*
* @param str
* @param onNoOnclickListener
*/
public void setNoOnclickListener(String str, onNoOnclickListener onNoOnclickListener) {
if (str != null) {
noStr = str;
}
this.noOnclickListener = onNoOnclickListener;
}
/**
* 设置确定按钮的显示内容和监听
*
* @param str
* @param onYesOnclickListener
*/
public void setYesOnclickListener(String str, onYesOnclickListener onYesOnclickListener) {
if (str != null) {
yesStr = str;
}
this.yesOnclickListener = onYesOnclickListener;
}
public SelfDialog(Context context) {
super(context, R.style.MyDialog);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.free_exercise_sure_dialog_layout);
//按空白处不能取消动画
setCanceledOnTouchOutside(false);
//初始化界面控件
initView();
//初始化界面数据
initData();
//初始化界面控件的事件
initEvent();
}
/**
* 初始化界面的确定和取消监听器
*/
private void initEvent() {
//设置确定按钮被点击后,向外界提供监听
yes.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (yesOnclickListener != null) {
yesOnclickListener.onYesClick();
}
}
});
//设置取消按钮被点击后,向外界提供监听
no.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (noOnclickListener != null) {
noOnclickListener.onNoClick();
}
}
});
}
/**
* 初始化界面控件的显示数据
*/
private void initData() {
//如果用户自定了title和message
if (titleStr != null) {
titleTv.setText(titleStr);
}
// messageStr= getContext().getString(R.string.tv_privacy_agreement);
messageTv.setMovementMethod(LinkMovementMethod.getInstance());
// messageTv.setText(Html.fromHtml(messageStr));
messageTv.setText(messageStr);
//如果设置按钮的文字
if (yesStr != null) {
yes.setText(yesStr);
}
if (noStr != null) {
no.setText(noStr);
}
}
/**
* 初始化界面控件
*/
private void initView() {
yes = (Button) findViewById(R.id.yes);
no = (Button) findViewById(R.id.no);
titleTv = (TextView) findViewById(R.id.title);
messageTv = (TextView) findViewById(R.id.message);
}
/**
* 从外界Activity为Dialog设置标题
*
* @param title
*/
public void setTitle(String title) {
titleStr = title;
}
/**
* 从外界Activity为Dialog设置dialog的message
*
* @param message
*/
public void setMessage(String message) {
messageStr = message;
}
/**
* 设置确定按钮和取消被点击的接口
*/
public interface onYesOnclickListener {
public void onYesClick();
}
public interface onNoOnclickListener {
public void onNoClick();
}
}
使用:
private void showPermissionDialog(String msg) {
final SelfDialog selfDialog = new SelfDialog(MainActivity.this,msg);
selfDialog.setTitle("权限使用说明");
selfDialog.setYesOnclickListener(getString(R.string.tv_ok), new SelfDialog.onYesOnclickListener() {
@Override
public void onYesClick() {
showPrivacyDialog();
selfDialog.dismiss();
}
});
selfDialog.setNoOnclickListener(getString(R.string.cancel), new SelfDialog.onNoOnclickListener() {
@Override
public void onNoClick() {
// Toast.makeText(MainActivity.this,"点击了--取消--按钮",Toast.LENGTH_LONG).show();
finish();
selfDialog.dismiss();
}
});
selfDialog.show();
}
44.String转Map
private Map<String, String> convertToMap(String nsdServiceInfo) {
Map<String,String> map = new HashMap<>();
String[] parts = nsdServiceInfo.split(",");
for(String part : parts){
String[] keyValue = part.split(":");
if (keyValue.length == 2) {
map.put(keyValue[0].trim(), keyValue[1].trim());
}
}
return map;
}
45.使用okHttp发送请求
- 发送get请求:
private List<String> getDeviceIpListFromUrl(String pinCode) {
String url = "https://dev.casts.app/rel.gbl1/api/connect/"+pinCode;
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.addHeader("Content-Type","application/json").addHeader("Accept","application/json")
.get()
.build();
try {
Response response = okHttpClient.newCall(request).execute();
String result = response.body().string();
Log.d("LXP","getDeviceIpListFromUrl ---- device info :"+result);
} catch (IOException e) {
throw new RuntimeException(e);
}
return null;
}
- 发送post请求:
private List<String> post(String pinCode){
String url = "https://dev.casts.app/rel.gbl1/api/";
OkHttpClient okHttpClient = new OkHttpClient();
String json = "";
MediaType mediaType = MediaType.parse("application/json;charset=utf-8");
RequestBody requestBody = RequestBody.create(mediaType,jsonBody);
Request requst = new Request.Builder()
.url(url)
.post(requestBody)
.build();
try{
Response response = okHttpClient.newCall(request).execute();
String result = response.body().string();
Log.d("LXP","result:"+result);
}catch(IOException e){
throw new RuntimeException(e);
}
return null;
}
46.禁用暗色模式
1.在主Activity设置:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
2.在style文件设置forceDarkAllowed:
<style name="AppTheme_NoActionBar" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/red</item>
<item name="android:forceDarkAllowed" tools:targetApi="q">false</item>
</style>
47.安卓不同风味apk工具
- FlavorUtil.java
package com.eshare.api.utils;
import com.eshare.lib.BuildConfig;
import androidx.annotation.NonNull;
import java.security.PublicKey;
/**
* Created by StephenLee on 2019/12/3
*/
@SuppressWarnings("ConstantConditions")
public final class FlavorUtils {
interface Flavor {
String COMMON = "common";
String BOXLIGHT = "boxlight";
String HIKVISION = "hikvision";
String IMAGO = "imago";
String SHENXINFU = "shenxinfu";
String INNOVATEAM ="innovateam";
String CONMONGOOGLE="conmongoogle";
String BIRDCAST ="birdcast";
String SH ="sh";
String UHSHARE ="uhshare";
String CPCAST ="cpcast";
String ONESCREEN ="onescreen";
}
private static boolean isFlavorCommon = BuildConfig.FLAVOR.equals(Flavor.COMMON);
private static boolean isFlavorBoxlight = BuildConfig.FLAVOR.equals(Flavor.BOXLIGHT);
private static boolean isFlavorHikvision = BuildConfig.FLAVOR.equals(Flavor.HIKVISION);
private static boolean isFlavorIMAGO = BuildConfig.FLAVOR.equals(Flavor.IMAGO);
private static boolean isFlavorShenXinFu = BuildConfig.FLAVOR.equals(Flavor.SHENXINFU);
private static boolean isFlavorInnovateam = BuildConfig.FLAVOR.equals(Flavor.INNOVATEAM);
private static boolean isFlavorCpcast = BuildConfig.FLAVOR.equals(Flavor.CPCAST);
private static boolean isFlavorConmonGoogle =BuildConfig.FLAVOR.equals(Flavor.CONMONGOOGLE);
private static boolean isFlavorBirdCast = BuildConfig.FLAVOR.equals(Flavor.BIRDCAST);
private static boolean isFlavorSH = BuildConfig.FLAVOR.equals(Flavor.SH);
private static boolean isFlavorUhshare = BuildConfig.FLAVOR.equals(Flavor.UHSHARE);
private static boolean isFlavorOneScreen = BuildConfig.FLAVOR.equals(Flavor.ONESCREEN);
private FlavorUtils() {
}
public static boolean isFlavorCommon() {
return isFlavorCommon;
}
public static boolean isFlavorSH() {
return isFlavorSH;
}
public static boolean isUhshare(){
return isFlavorUhshare;
}
public static boolean isFlavorBirdCast(){
return isFlavorBirdCast;
}
public static boolean isFlavorBoxlight() {
return isFlavorBoxlight;
}
public static boolean isFlavorCpCast(){
return isFlavorCpcast;
}
public static boolean isFlavorHikvision() {
return isFlavorHikvision;
}
public static boolean isFlavorShenXinFu() {
return isFlavorShenXinFu;
}
public static boolean isFlavorIMAGO() {
return isFlavorIMAGO;
}
public static boolean isFlavorInnovateam() {
return isFlavorInnovateam;
}
public static boolean isFlavorOneScreen() {
return isFlavorOneScreen;
}
public static boolean isFlavorConmongoogle() {
return isFlavorConmonGoogle;
}
@NonNull
public static String getDefaultAppName() {
if (isFlavorBoxlight) {
return "Unplug'd";
} else if (isFlavorHikvision) {
return "HIK-share";
}
return "EShare";
}
@NonNull
public static String getFindDeviceCommand() {
return isFlavorBoxlight ? "FindBoxlight" : "FindECloudBox";
}
@NonNull
public static String getReplyDeviceCommand() {
return isFlavorBoxlight ? "BoxlightPro" : "ECloudBox516Pro";
}
public static boolean isMultiScreen4Supported() {
return true;
}
// 投屏时画笔是否隐藏
public static boolean isPenHideWhenCasting(){
return isFlavorBoxlight;
}
}
- build.gradle文件
productFlavors {
common {
//yes ota
applicationId "com.ecloud.eshare"
dimension 'default'
}
birdcast {
//yes ota
applicationId "com.ecloud.birdcast"
dimension 'default'
}
hikvision {
//yes ota
applicationId "com.hik.hikshare"
dimension 'default'
}
boe {
//no ota
dimension 'default'
}
shenxinfu {
applicationId "com.ecloud.sangfor"
dimension 'default'
}
innovateam {
//no ota
applicationId "com.ecloud.incast"
dimension 'default'
}
boxlight {
// no ota
applicationId "com.boxlight.unplugd"
dimension 'default'
}
meishuo {
//no ota
applicationId "com.ecloud.meishuo"
dimension 'default'
}
conmongoogle {
applicationId "com.eshare.clientv2"
dimension 'default'
}
imago {
// no ota
applicationId "com.ecloud.imago"
dimension 'default'
}
sh {
// no ota
applicationId "com.ecloud.sh"
dimension 'default'
}
uhshare {
// no ota
applicationId "com.ecloud.uhshare"
dimension 'default'
}
cpcast {
applicationId "com.ecloud.cpcast"
dimension 'default'
versionCode getCpVersionCode()
versionName getCpVersionName()
}
aetouch{
applicationId "com.ecloud.aetouch"
dimension 'default'
}
onescreen{
applicationId "com.ecloud.onescreen"
dimension 'default'
}
}
完整build文件
apply plugin: 'com.android.application'
android {
signingConfigs {
// 签名文件信息,用于命令行编译。将发布密钥和密钥库的密码放在构建文件中并不安全。作为替代方案,
// 您可以将此构建文件配置为通过环境变量获取这些密码,或让构建流程提示您输入这些密码。
config {
keyPassword System.getenv("KEYPWD")
storeFile file('D:/Signature_File/release.keystore')
storePassword System.getenv("KSTOREPWD")
keyAlias 'myandroidkey'
// storePassword System.console().readLine("\nKeystore password: ")
// keyPassword System.console().readLine("\nKey password: ")
}
}
compileSdkVersion 31
buildToolsVersion '30.0.3'
flavorDimensions 'default'
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
variant.outputs.all { output ->
if (outputFileName.endsWith('.apk')) {
if(flavorName=='hikvision'){
outputFileName = "HIK-shareClient for Android_${defaultConfig.versionName}.apk"
}else if(flavorName=='innovateam'){
outputFileName = "InCast for Android_${defaultConfig.versionName}.apk"
}else if(flavorName=='birdcast'){
outputFileName = "BirdCast for Android_${defaultConfig.versionName}.apk"
}else if(flavorName=='shenxinfu'){
outputFileName = "ShenXinFu for Android_${defaultConfig.versionName}.apk"
}else if (flavorName=='sh'){
outputFileName = "SH for Android_${defaultConfig.versionName}.apk"
}else if(flavorName=='cpcast'){
outputFileName = "CpCast for Android_${getCpVersionName()}.apk"
}else if(flavorName=='aetouch'){
outputFileName = "AETouch for Android_${defaultConfig.versionName}.apk"
}else if(flavorName=='onescreen'){
outputFileName = "OneScreen EShare for Android_${defaultConfig.versionName}.apk"
}
else{
outputFileName = "EShare for Android_${defaultConfig.versionName}.apk"
}
}
}
}
}
defaultConfig {
applicationId "com.ecloud.eshare"
minSdkVersion 19
//noinspection ExpiredTargetSdkVersion,OldTargetApi
targetSdkVersion 30
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
ndk {
abiFilters 'armeabi', 'arm64-v8a'
// abiFilters 'arm64-v8a'
}
multiDexEnabled true
}
buildTypes {
debug {
minifyEnabled false
// 在layout中使用@string/app_version来显示版本
resValue "string", "app_version", "${defaultConfig.versionName}"
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
resValue "string", "app_version", "${defaultConfig.versionName}"
signingConfig signingConfigs.config
}
}
productFlavors {
common {
//yes ota
applicationId "com.ecloud.eshare"
dimension 'default'
}
birdcast {
//yes ota
applicationId "com.ecloud.birdcast"
dimension 'default'
}
hikvision {
//yes ota
applicationId "com.hik.hikshare"
dimension 'default'
}
boe {
//no ota
dimension 'default'
}
shenxinfu {
applicationId "com.ecloud.sangfor"
dimension 'default'
}
innovateam {
//no ota
applicationId "com.ecloud.incast"
dimension 'default'
}
boxlight {
// no ota
applicationId "com.boxlight.unplugd"
dimension 'default'
}
meishuo {
//no ota
applicationId "com.ecloud.meishuo"
dimension 'default'
}
conmongoogle {
applicationId "com.eshare.clientv2"
dimension 'default'
}
imago {
// no ota
applicationId "com.ecloud.imago"
dimension 'default'
}
sh {
// no ota
applicationId "com.ecloud.sh"
dimension 'default'
}
uhshare {
// no ota
applicationId "com.ecloud.uhshare"
dimension 'default'
}
cpcast {
applicationId "com.ecloud.cpcast"
dimension 'default'
versionCode getCpVersionCode()
versionName getCpVersionName()
}
aetouch{
applicationId "com.ecloud.aetouch"
dimension 'default'
}
onescreen{
applicationId "com.ecloud.onescreen"
dimension 'default'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
testOptions {
unitTests.includeAndroidResources = true
}
packagingOptions {
doNotStrip "*/armeabi/*.so"
doNotStrip "*/armeabi-v7a/*.so"
exclude 'META-INF/*'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/INDEX.LIST'
}
}
static int getCpVersionCode() {
return new Date().format('yyyyMMdd', TimeZone.getDefault()).toInteger()
}
static String getCpVersionName() {
return "v2.0." + new Date().format("Mdd", TimeZone.getDefault())
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.aar'])
implementation 'com.google.code.gson:gson:2.8.5'
// OkHttp
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation project(':eShareLibrary')
implementation 'com.google.android.material:material:1.0.0'
testImplementation 'junit:junit:4.12'
// Optional -- Robolectric environment
}
48.Map转Json
private String mapToJson(Map<String, Object> map) {
JSONObject json = new JSONObject();
for(Map.Entry<String,Object> entry : map.entrySet()){
String key = entry.getKey();
try {
json.put(key,entry.getValue());
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
return json.toString();
}
- String 转 JSONObject
JSONObject jsonObject = new JSONObject(result);
JSONObject ipJson = (JSONObject) jsonObject.get("data");
JSONArray ipList = (JSONArray) ipJson.get("ip_list");
49.排序算法
- 快速排序:
function sort(arr, startIndex, endIndex) {
// 递归结束条件:startIndex大于等于endIndex的时候
if (startIndex >= endIndex) {
return;
}
// 得到基准元素的位置
let pivotIndex = partition(arr, startIndex, endIndex);
sort(arr, startIndex, pivotIndex - 1);
sort(arr, pivotIndex + 1, endIndex);
}
function partition(arr, startIndex, endIndex) {
// 选择第一个位置的元素作为基准元素
let pivot = arr[startIndex];
let left = startIndex;
let right = endIndex;
let index = startIndex;
// 外循环在左右指针重合或者交错的时候结束
while (right > left) {
// right指针从右向左进行比较
while (right > left) {
if (arr[right] < pivot) {
arr[left] = arr[right];
index = right;
left++;
break;
}
right--;
}
// left指针从左向右进行比较
while (right > left) {
if (arr[left] > pivot) {
arr[right] = arr[left];
index = left;
right--;
break;
}
left++;
}
}
arr[index] = pivot;
return index;
}
let arr = [4, 5, 8, 1, 7, 2, 6, 3];
sort(arr, 0, arr.length - 1);
console.log(arr);
50.PowerManagerService
Google Git
Sign in
android / platform / frameworks / base / 84e2756c0f3794c6efe5568a9d09101ba689fb39 / . / services / java / com / android / server / power / PowerManagerService.java
blob: 5a5d91000ef14968a2e8a819225850576bbdf811 [file] [log] [blame]
/*
* Copyright (C) 2007 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.android.server.power;
import com.android.internal.app.IBatteryStats;
import com.android.server.BatteryService;
import com.android.server.EventLogTags;
import com.android.server.LightsService;
import com.android.server.TwilightService;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService;
import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemService;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
import android.view.WindowManagerPolicy;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import libcore.util.Objects;
/**
* The power manager service is responsible for coordinating power management
* functions on the device.
*/
public final class PowerManagerService extends IPowerManager.Stub
implements Watchdog.Monitor {
private static final String TAG = "PowerManagerService";
private static final boolean DEBUG = false;
private static final boolean DEBUG_SPEW = DEBUG && true;
// Message: Sent when a user activity timeout occurs to update the power state.
private static final int MSG_USER_ACTIVITY_TIMEOUT = 1;
// Message: Sent when the device enters or exits a napping or dreaming state.
private static final int MSG_SANDMAN = 2;
// Message: Sent when the screen on blocker is released.
private static final int MSG_SCREEN_ON_BLOCKER_RELEASED = 3;
// Message: Sent to poll whether the boot animation has terminated.
private static final int MSG_CHECK_IF_BOOT_ANIMATION_FINISHED = 4;
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
// Dirty bit: mWakefulness changed
private static final int DIRTY_WAKEFULNESS = 1 << 1;
// Dirty bit: user activity was poked or may have timed out
private static final int DIRTY_USER_ACTIVITY = 1 << 2;
// Dirty bit: actual display power state was updated asynchronously
private static final int DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED = 1 << 3;
// Dirty bit: mBootCompleted changed
private static final int DIRTY_BOOT_COMPLETED = 1 << 4;
// Dirty bit: settings changed
private static final int DIRTY_SETTINGS = 1 << 5;
// Dirty bit: mIsPowered changed
private static final int DIRTY_IS_POWERED = 1 << 6;
// Dirty bit: mStayOn changed
private static final int DIRTY_STAY_ON = 1 << 7;
// Dirty bit: battery state changed
private static final int DIRTY_BATTERY_STATE = 1 << 8;
// Dirty bit: proximity state changed
private static final int DIRTY_PROXIMITY_POSITIVE = 1 << 9;
// Dirty bit: screen on blocker state became held or unheld
private static final int DIRTY_SCREEN_ON_BLOCKER_RELEASED = 1 << 10;
// Dirty bit: dock state changed
private static final int DIRTY_DOCK_STATE = 1 << 11;
// Wakefulness: The device is asleep and can only be awoken by a call to wakeUp().
// The screen should be off or in the process of being turned off by the display controller.
private static final int WAKEFULNESS_ASLEEP = 0;
// Wakefulness: The device is fully awake. It can be put to sleep by a call to goToSleep().
// When the user activity timeout expires, the device may start napping or go to sleep.
private static final int WAKEFULNESS_AWAKE = 1;
// Wakefulness: The device is napping. It is deciding whether to dream or go to sleep
// but hasn't gotten around to it yet. It can be awoken by a call to wakeUp(), which
// ends the nap. User activity may brighten the screen but does not end the nap.
private static final int WAKEFULNESS_NAPPING = 2;
// Wakefulness: The device is dreaming. It can be awoken by a call to wakeUp(),
// which ends the dream. The device goes to sleep when goToSleep() is called, when
// the dream ends or when unplugged.
// User activity may brighten the screen but does not end the dream.
private static final int WAKEFULNESS_DREAMING = 3;
// Summarizes the state of all active wakelocks.
private static final int WAKE_LOCK_CPU = 1 << 0;
private static final int WAKE_LOCK_SCREEN_BRIGHT = 1 << 1;
private static final int WAKE_LOCK_SCREEN_DIM = 1 << 2;
private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3;
private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4;
private static final int WAKE_LOCK_STAY_AWAKE = 1 << 5; // only set if already awake
// Summarizes the user activity state.
private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;
private static final int USER_ACTIVITY_SCREEN_DIM = 1 << 1;
// Default and minimum screen off timeout in milliseconds.
private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000;
private static final int MINIMUM_SCREEN_OFF_TIMEOUT = 10 * 1000;
// The screen dim duration, in milliseconds.
// This is subtracted from the end of the screen off timeout so the
// minimum screen off timeout should be longer than this.
private static final int SCREEN_DIM_DURATION = 7 * 1000;
// The maximum screen dim time expressed as a ratio relative to the screen
// off timeout. If the screen off timeout is very short then we want the
// dim timeout to also be quite short so that most of the time is spent on.
// Otherwise the user won't get much screen on time before dimming occurs.
private static final float MAXIMUM_SCREEN_DIM_RATIO = 0.2f;
// Upper bound on the battery charge percentage in order to consider turning
// the screen on when the device starts charging wirelessly.
// See point of use for more details.
private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95;
// The name of the boot animation service in init.rc.
private static final String BOOT_ANIMATION_SERVICE = "bootanim";
// Poll interval in milliseconds for watching boot animation finished.
private static final int BOOT_ANIMATION_POLL_INTERVAL = 200;
// If the battery level drops by this percentage and the user activity timeout
// has expired, then assume the device is receiving insufficient current to charge
// effectively and terminate the dream.
private static final int DREAM_BATTERY_LEVEL_DRAIN_CUTOFF = 5;
private Context mContext;
private LightsService mLightsService;
private BatteryService mBatteryService;
private DisplayManagerService mDisplayManagerService;
private IBatteryStats mBatteryStats;
private HandlerThread mHandlerThread;
private PowerManagerHandler mHandler;
private WindowManagerPolicy mPolicy;
private Notifier mNotifier;
private DisplayPowerController mDisplayPowerController;
private SettingsObserver mSettingsObserver;
private DreamManagerService mDreamManager;
private LightsService.Light mAttentionLight;
private final Object mLock = new Object();
// A bitfield that indicates what parts of the power state have
// changed and need to be recalculated.
private int mDirty;
// Indicates whether the device is awake or asleep or somewhere in between.
// This is distinct from the screen power state, which is managed separately.
private int mWakefulness;
// True if MSG_SANDMAN has been scheduled.
private boolean mSandmanScheduled;
// Table of all suspend blockers.
// There should only be a few of these.
private final ArrayList<SuspendBlocker> mSuspendBlockers = new ArrayList<SuspendBlocker>();
// Table of all wake locks acquired by applications.
private final ArrayList<WakeLock> mWakeLocks = new ArrayList<WakeLock>();
// A bitfield that summarizes the state of all active wakelocks.
private int mWakeLockSummary;
// If true, instructs the display controller to wait for the proximity sensor to
// go negative before turning the screen on.
private boolean mRequestWaitForNegativeProximity;
// Timestamp of the last time the device was awoken or put to sleep.
private long mLastWakeTime;
private long mLastSleepTime;
// True if we need to send a wake up or go to sleep finished notification
// when the display is ready.
private boolean mSendWakeUpFinishedNotificationWhenReady;
private boolean mSendGoToSleepFinishedNotificationWhenReady;
// Timestamp of the last call to user activity.
private long mLastUserActivityTime;
private long mLastUserActivityTimeNoChangeLights;
// A bitfield that summarizes the effect of the user activity timer.
// A zero value indicates that the user activity timer has expired.
private int mUserActivitySummary;
// The desired display power state. The actual state may lag behind the
// requested because it is updated asynchronously by the display power controller.
private final DisplayPowerRequest mDisplayPowerRequest = new DisplayPowerRequest();
// The time the screen was last turned off, in elapsedRealtime() timebase.
private long mLastScreenOffEventElapsedRealTime;
// True if the display power state has been fully applied, which means the display
// is actually on or actually off or whatever was requested.
private boolean mDisplayReady;
// True if holding a wake-lock to block suspend of the CPU.
private boolean mHoldingWakeLockSuspendBlocker;
// The suspend blocker used to keep the CPU alive when wake locks have been acquired.
private final SuspendBlocker mWakeLockSuspendBlocker;
// The screen on blocker used to keep the screen from turning on while the lock
// screen is coming up.
private final ScreenOnBlockerImpl mScreenOnBlocker;
// The display blanker used to turn the screen on or off.
private final DisplayBlankerImpl mDisplayBlanker;
// True if systemReady() has been called.
private boolean mSystemReady;
// True if boot completed occurred. We keep the screen on until this happens.
private boolean mBootCompleted;
// True if the device is plugged into a power source.
private boolean mIsPowered;
// The current plug type, such as BatteryManager.BATTERY_PLUGGED_WIRELESS.
private int mPlugType;
// The current battery level percentage.
private int mBatteryLevel;
// The battery level percentage at the time the dream started.
// This is used to terminate a dream and go to sleep if the battery is
// draining faster than it is charging and the user activity timeout has expired.
private int mBatteryLevelWhenDreamStarted;
// The current dock state.
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
// True if the device should wake up when plugged or unplugged.
private boolean mWakeUpWhenPluggedOrUnpluggedConfig;
// True if dreams are supported on this device.
private boolean mDreamsSupportedConfig;
// Default value for dreams enabled
private boolean mDreamsEnabledByDefaultConfig;
// Default value for dreams activate-on-sleep
private boolean mDreamsActivatedOnSleepByDefaultConfig;
// Default value for dreams activate-on-dock
private boolean mDreamsActivatedOnDockByDefaultConfig;
// True if dreams are enabled by the user.
private boolean mDreamsEnabledSetting;
// True if dreams should be activated on sleep.
private boolean mDreamsActivateOnSleepSetting;
// True if dreams should be activated on dock.
private boolean mDreamsActivateOnDockSetting;
// The screen off timeout setting value in milliseconds.
private int mScreenOffTimeoutSetting;
// The maximum allowable screen off timeout according to the device
// administration policy. Overrides other settings.
private int mMaximumScreenOffTimeoutFromDeviceAdmin = Integer.MAX_VALUE;
// The stay on while plugged in setting.
// A bitfield of battery conditions under which to make the screen stay on.
private int mStayOnWhilePluggedInSetting;
// True if the device should stay on.
private boolean mStayOn;
// True if the proximity sensor reads a positive result.
private boolean mProximityPositive;
// Screen brightness setting limits.
private int mScreenBrightnessSettingMinimum;
private int mScreenBrightnessSettingMaximum;
private int mScreenBrightnessSettingDefault;
// The screen brightness setting, from 0 to 255.
// Use -1 if no value has been set.
private int mScreenBrightnessSetting;
// The screen auto-brightness adjustment setting, from -1 to 1.
// Use 0 if there is no adjustment.
private float mScreenAutoBrightnessAdjustmentSetting;
// The screen brightness mode.
// One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants.
private int mScreenBrightnessModeSetting;
// The screen brightness setting override from the window manager
// to allow the current foreground activity to override the brightness.
// Use -1 to disable.
private int mScreenBrightnessOverrideFromWindowManager = -1;
// The user activity timeout override from the window manager
// to allow the current foreground activity to override the user activity timeout.
// Use -1 to disable.
private long mUserActivityTimeoutOverrideFromWindowManager = -1;
// The screen brightness setting override from the settings application
// to temporarily adjust the brightness until next updated,
// Use -1 to disable.
private int mTemporaryScreenBrightnessSettingOverride = -1;
// The screen brightness adjustment setting override from the settings
// application to temporarily adjust the auto-brightness adjustment factor
// until next updated, in the range -1..1.
// Use NaN to disable.
private float mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN;
// Time when we last logged a warning about calling userActivity() without permission.
private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE;
private native void nativeInit();
private static native void nativeShutdown();
private static native void nativeReboot(String reason) throws IOException;
private static native void nativeSetPowerState(boolean screenOn, boolean screenBright);
private static native void nativeAcquireSuspendBlocker(String name);
private static native void nativeReleaseSuspendBlocker(String name);
private static native void nativeSetInteractive(boolean enable);
private static native void nativeSetAutoSuspend(boolean enable);
public PowerManagerService() {
synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService");
mWakeLockSuspendBlocker.acquire();
mScreenOnBlocker = new ScreenOnBlockerImpl();
mDisplayBlanker = new DisplayBlankerImpl();
mHoldingWakeLockSuspendBlocker = true;
mWakefulness = WAKEFULNESS_AWAKE;
}
nativeInit();
nativeSetPowerState(true, true);
}
/**
* Initialize the power manager.
* Must be called before any other functions within the power manager are called.
*/
public void init(Context context, LightsService ls,
ActivityManagerService am, BatteryService bs, IBatteryStats bss,
DisplayManagerService dm) {
mContext = context;
mLightsService = ls;
mBatteryService = bs;
mBatteryStats = bss;
mDisplayManagerService = dm;
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addMonitor(this);
// Forcibly turn the screen on at boot so that it is in a known power state.
// We do this in init() rather than in the constructor because setting the
// screen state requires a call into surface flinger which then needs to call back
// into the activity manager to check permissions. Unfortunately the
// activity manager is not running when the constructor is called, so we
// have to defer setting the screen state until this point.
mDisplayBlanker.unblankAllDisplays();
}
public void setPolicy(WindowManagerPolicy policy) {
synchronized (mLock) {
mPolicy = policy;
}
}
public void systemReady(TwilightService twilight, DreamManagerService dreamManager) {
synchronized (mLock) {
mSystemReady = true;
mDreamManager = dreamManager;
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
// The notifier runs on the system server's main looper so as not to interfere
// with the animations and other critical functions of the power manager.
mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
mScreenOnBlocker, mPolicy);
// The display power controller runs on the power manager service's
// own handler thread.
mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
mContext, mNotifier, mLightsService, twilight, mDisplayManagerService,
mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION);
// Register for broadcasts from other components of the system.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(new BootCompletedReceiver(), filter, null, mHandler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_DREAMING_STARTED);
filter.addAction(Intent.ACTION_DREAMING_STOPPED);
mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_DOCK_EVENT);
mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
// Register for settings changes.
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SCREENSAVER_ENABLED),
false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP),
false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK),
false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_OFF_TIMEOUT),
false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.STAY_ON_WHILE_PLUGGED_IN),
false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_BRIGHTNESS),
false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_BRIGHTNESS_MODE),
false, mSettingsObserver, UserHandle.USER_ALL);
// Go.
readConfigurationLocked();
updateSettingsLocked();
mDirty |= DIRTY_BATTERY_STATE;
updatePowerStateLocked();
}
}
private void readConfigurationLocked() {
final Resources resources = mContext.getResources();
mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean(
com.android.internal.R.bool.config_unplugTurnsOnScreen);
mDreamsSupportedConfig = resources.getBoolean(
com.android.internal.R.bool.config_dreamsSupported);
mDreamsEnabledByDefaultConfig = resources.getBoolean(
com.android.internal.R.bool.config_dreamsEnabledByDefault);
mDreamsActivatedOnSleepByDefaultConfig = resources.getBoolean(
com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
mDreamsActivatedOnDockByDefaultConfig = resources.getBoolean(
com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
}
private void updateSettingsLocked() {
final ContentResolver resolver = mContext.getContentResolver();
mDreamsEnabledSetting = (Settings.Secure.getIntForUser(resolver,
Settings.Secure.SCREENSAVER_ENABLED,
mDreamsEnabledByDefaultConfig ? 1 : 0,
UserHandle.USER_CURRENT) != 0);
mDreamsActivateOnSleepSetting = (Settings.Secure.getIntForUser(resolver,
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
mDreamsActivatedOnSleepByDefaultConfig ? 1 : 0,
UserHandle.USER_CURRENT) != 0);
mDreamsActivateOnDockSetting = (Settings.Secure.getIntForUser(resolver,
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
mDreamsActivatedOnDockByDefaultConfig ? 1 : 0,
UserHandle.USER_CURRENT) != 0);
mScreenOffTimeoutSetting = Settings.System.getIntForUser(resolver,
Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT,
UserHandle.USER_CURRENT);
mStayOnWhilePluggedInSetting = Settings.Global.getInt(resolver,
Settings.Global.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC);
final int oldScreenBrightnessSetting = mScreenBrightnessSetting;
mScreenBrightnessSetting = Settings.System.getIntForUser(resolver,
Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessSettingDefault,
UserHandle.USER_CURRENT);
if (oldScreenBrightnessSetting != mScreenBrightnessSetting) {
mTemporaryScreenBrightnessSettingOverride = -1;
}
final float oldScreenAutoBrightnessAdjustmentSetting =
mScreenAutoBrightnessAdjustmentSetting;
mScreenAutoBrightnessAdjustmentSetting = Settings.System.getFloatForUser(resolver,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f,
UserHandle.USER_CURRENT);
if (oldScreenAutoBrightnessAdjustmentSetting != mScreenAutoBrightnessAdjustmentSetting) {
mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN;
}
mScreenBrightnessModeSetting = Settings.System.getIntForUser(resolver,
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
mDirty |= DIRTY_SETTINGS;
}
private void handleSettingsChangedLocked() {
updateSettingsLocked();
updatePowerStateLocked();
}
@Override // Binder call
public void acquireWakeLock(IBinder lock, int flags, String tag, WorkSource ws) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
PowerManager.validateWakeLockParameters(flags, tag);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
if (ws != null && ws.size() != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.UPDATE_DEVICE_STATS, null);
} else {
ws = null;
}
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
final long ident = Binder.clearCallingIdentity();
try {
acquireWakeLockInternal(lock, flags, tag, ws, uid, pid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void acquireWakeLockInternal(IBinder lock, int flags, String tag, WorkSource ws,
int uid, int pid) {
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "acquireWakeLockInternal: lock=" + Objects.hashCode(lock)
+ ", flags=0x" + Integer.toHexString(flags)
+ ", tag=\"" + tag + "\", ws=" + ws + ", uid=" + uid + ", pid=" + pid);
}
WakeLock wakeLock;
int index = findWakeLockIndexLocked(lock);
if (index >= 0) {
wakeLock = mWakeLocks.get(index);
if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
// Update existing wake lock. This shouldn't happen but is harmless.
notifyWakeLockReleasedLocked(wakeLock);
wakeLock.updateProperties(flags, tag, ws, uid, pid);
notifyWakeLockAcquiredLocked(wakeLock);
}
} else {
wakeLock = new WakeLock(lock, flags, tag, ws, uid, pid);
try {
lock.linkToDeath(wakeLock, 0);
} catch (RemoteException ex) {
throw new IllegalArgumentException("Wake lock is already dead.");
}
notifyWakeLockAcquiredLocked(wakeLock);
mWakeLocks.add(wakeLock);
}
applyWakeLockFlagsOnAcquireLocked(wakeLock);
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();
}
}
private static boolean isScreenLock(final WakeLock wakeLock) {
switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.FULL_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
return true;
}
return false;
}
private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock) {
if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0 &&
isScreenLock(wakeLock)) {
wakeUpNoUpdateLocked(SystemClock.uptimeMillis());
}
}
@Override // Binder call
public void releaseWakeLock(IBinder lock, int flags) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
final long ident = Binder.clearCallingIdentity();
try {
releaseWakeLockInternal(lock, flags);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void releaseWakeLockInternal(IBinder lock, int flags) {
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "releaseWakeLockInternal: lock=" + Objects.hashCode(lock)
+ ", flags=0x" + Integer.toHexString(flags));
}
int index = findWakeLockIndexLocked(lock);
if (index < 0) {
return;
}
WakeLock wakeLock = mWakeLocks.get(index);
mWakeLocks.remove(index);
notifyWakeLockReleasedLocked(wakeLock);
wakeLock.mLock.unlinkToDeath(wakeLock, 0);
if ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0) {
mRequestWaitForNegativeProximity = true;
}
applyWakeLockFlagsOnReleaseLocked(wakeLock);
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();
}
}
private void handleWakeLockDeath(WakeLock wakeLock) {
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "handleWakeLockDeath: lock=" + Objects.hashCode(wakeLock.mLock));
}
int index = mWakeLocks.indexOf(wakeLock);
if (index < 0) {
return;
}
mWakeLocks.remove(index);
notifyWakeLockReleasedLocked(wakeLock);
applyWakeLockFlagsOnReleaseLocked(wakeLock);
mDirty |= DIRTY_WAKE_LOCKS;
updatePowerStateLocked();
}
}
private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0) {
userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER,
PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
wakeLock.mOwnerUid);
}
}
@Override // Binder call
public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
if (ws != null && ws.size() != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.UPDATE_DEVICE_STATS, null);
} else {
ws = null;
}
final long ident = Binder.clearCallingIdentity();
try {
updateWakeLockWorkSourceInternal(lock, ws);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void updateWakeLockWorkSourceInternal(IBinder lock, WorkSource ws) {
synchronized (mLock) {
int index = findWakeLockIndexLocked(lock);
if (index < 0) {
throw new IllegalArgumentException("Wake lock not active");
}
WakeLock wakeLock = mWakeLocks.get(index);
if (!wakeLock.hasSameWorkSource(ws)) {
notifyWakeLockReleasedLocked(wakeLock);
wakeLock.updateWorkSource(ws);
notifyWakeLockAcquiredLocked(wakeLock);
}
}
}
private int findWakeLockIndexLocked(IBinder lock) {
final int count = mWakeLocks.size();
for (int i = 0; i < count; i++) {
if (mWakeLocks.get(i).mLock == lock) {
return i;
}
}
return -1;
}
private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
if (mSystemReady) {
mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
}
}
private void notifyWakeLockReleasedLocked(WakeLock wakeLock) {
if (mSystemReady) {
mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag,
wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
}
}
@Override // Binder call
public boolean isWakeLockLevelSupported(int level) {
final long ident = Binder.clearCallingIdentity();
try {
return isWakeLockLevelSupportedInternal(level);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private boolean isWakeLockLevelSupportedInternal(int level) {
synchronized (mLock) {
switch (level) {
case PowerManager.PARTIAL_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
case PowerManager.FULL_WAKE_LOCK:
return true;
case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
return mSystemReady && mDisplayPowerController.isProximitySensorAvailable();
default:
return false;
}
}
}
@Override // Binder call
public void userActivity(long eventTime, int event, int flags) {
final long now = SystemClock.uptimeMillis();
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED) {
// Once upon a time applications could call userActivity().
// Now we require the DEVICE_POWER permission. Log a warning and ignore the
// request instead of throwing a SecurityException so we don't break old apps.
synchronized (mLock) {
if (now >= mLastWarningAboutUserActivityPermission + (5 * 60 * 1000)) {
mLastWarningAboutUserActivityPermission = now;
Slog.w(TAG, "Ignoring call to PowerManager.userActivity() because the "
+ "caller does not have DEVICE_POWER permission. "
+ "Please fix your app! "
+ " pid=" + Binder.getCallingPid()
+ " uid=" + Binder.getCallingUid());
}
}
return;
}
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
userActivityInternal(eventTime, event, flags, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
// Called from native code.
private void userActivityFromNative(long eventTime, int event, int flags) {
userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID);
}
private void userActivityInternal(long eventTime, int event, int flags, int uid) {
synchronized (mLock) {
if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
updatePowerStateLocked();
}
}
}
private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "userActivityNoUpdateLocked: eventTime=" + eventTime
+ ", event=" + event + ", flags=0x" + Integer.toHexString(flags)
+ ", uid=" + uid);
}
if (eventTime < mLastSleepTime || eventTime < mLastWakeTime
|| mWakefulness == WAKEFULNESS_ASLEEP || !mBootCompleted || !mSystemReady) {
return false;
}
mNotifier.onUserActivity(event, uid);
if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
if (eventTime > mLastUserActivityTimeNoChangeLights
&& eventTime > mLastUserActivityTime) {
mLastUserActivityTimeNoChangeLights = eventTime;
mDirty |= DIRTY_USER_ACTIVITY;
return true;
}
} else {
if (eventTime > mLastUserActivityTime) {
mLastUserActivityTime = eventTime;
mDirty |= DIRTY_USER_ACTIVITY;
return true;
}
}
return false;
}
@Override // Binder call
public void wakeUp(long eventTime) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
wakeUpInternal(eventTime);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
// Called from native code.
private void wakeUpFromNative(long eventTime) {
wakeUpInternal(eventTime);
}
private void wakeUpInternal(long eventTime) {
synchronized (mLock) {
if (wakeUpNoUpdateLocked(eventTime)) {
updatePowerStateLocked();
}
}
}
private boolean wakeUpNoUpdateLocked(long eventTime) {
if (DEBUG_SPEW) {
Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime);
}
if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
|| !mBootCompleted || !mSystemReady) {
return false;
}
switch (mWakefulness) {
case WAKEFULNESS_ASLEEP:
Slog.i(TAG, "Waking up from sleep...");
sendPendingNotificationsLocked();
mNotifier.onWakeUpStarted();
mSendWakeUpFinishedNotificationWhenReady = true;
break;
case WAKEFULNESS_DREAMING:
Slog.i(TAG, "Waking up from dream...");
break;
case WAKEFULNESS_NAPPING:
Slog.i(TAG, "Waking up from nap...");
break;
}
mLastWakeTime = eventTime;
mWakefulness = WAKEFULNESS_AWAKE;
mDirty |= DIRTY_WAKEFULNESS;
userActivityNoUpdateLocked(
eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
return true;
}
/*SystemClock.uptimeMillis()是Android中的一个方法,用于获取从设备启动到现在的时间(以毫秒为单位)。这与System.currentTimeMillis()不同,后者返回的是自1970年1月1日00:00:00 GMT以来的当前时间。*/
@Override // Binder call
public void goToSleep(long eventTime, int reason) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
goToSleepInternal(eventTime, reason);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
// Called from native code.
private void goToSleepFromNative(long eventTime, int reason) {
goToSleepInternal(eventTime, reason);
}
private void goToSleepInternal(long eventTime, int reason) {
synchronized (mLock) {
if (goToSleepNoUpdateLocked(eventTime, reason)) {
updatePowerStateLocked();
}
}
}
private boolean goToSleepNoUpdateLocked(long eventTime, int reason) {
if (DEBUG_SPEW) {
Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime + ", reason=" + reason);
}
if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP
|| !mBootCompleted || !mSystemReady) {
return false;
}
switch (reason) {
case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
Slog.i(TAG, "Going to sleep due to device administration policy...");
break;
case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
Slog.i(TAG, "Going to sleep due to screen timeout...");
break;
default:
Slog.i(TAG, "Going to sleep by user request...");
reason = PowerManager.GO_TO_SLEEP_REASON_USER;
break;
}
sendPendingNotificationsLocked();
mNotifier.onGoToSleepStarted(reason);
mSendGoToSleepFinishedNotificationWhenReady = true;
mLastSleepTime = eventTime;
mDirty |= DIRTY_WAKEFULNESS;
mWakefulness = WAKEFULNESS_ASLEEP;
// Report the number of wake locks that will be cleared by going to sleep.
int numWakeLocksCleared = 0;
final int numWakeLocks = mWakeLocks.size();
for (int i = 0; i < numWakeLocks; i++) {
final WakeLock wakeLock = mWakeLocks.get(i);
switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.FULL_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
numWakeLocksCleared += 1;
break;
}
}
EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared);
return true;
}
@Override // Binder call
public void nap(long eventTime) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
napInternal(eventTime);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void napInternal(long eventTime) {
synchronized (mLock) {
if (napNoUpdateLocked(eventTime)) {
updatePowerStateLocked();
}
}
}
private boolean napNoUpdateLocked(long eventTime) {
if (DEBUG_SPEW) {
Slog.d(TAG, "napNoUpdateLocked: eventTime=" + eventTime);
}
if (eventTime < mLastWakeTime || mWakefulness != WAKEFULNESS_AWAKE
|| !mBootCompleted || !mSystemReady) {
return false;
}
Slog.i(TAG, "Nap time...");
mDirty |= DIRTY_WAKEFULNESS;
mWakefulness = WAKEFULNESS_NAPPING;
return true;
}
/**
* Updates the global power state based on dirty bits recorded in mDirty.
*
* This is the main function that performs power state transitions.
* We centralize them here so that we can recompute the power state completely
* each time something important changes, and ensure that we do it the same
* way each time. The point is to gather all of the transition logic here.
*/
private void updatePowerStateLocked() {
if (!mSystemReady || mDirty == 0) {
return;
}
// Phase 0: Basic state updates.
updateIsPoweredLocked(mDirty);
updateStayOnLocked(mDirty);
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = SystemClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
// Phase 2: Update dreams and display power state.
updateDreamLocked(dirtyPhase2);
updateDisplayPowerStateLocked(dirtyPhase2);
// Phase 3: Send notifications, if needed.
if (mDisplayReady) {
sendPendingNotificationsLocked();
}
// Phase 4: Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
updateSuspendBlockerLocked();
}
private void sendPendingNotificationsLocked() {
if (mSendWakeUpFinishedNotificationWhenReady) {
mSendWakeUpFinishedNotificationWhenReady = false;
mNotifier.onWakeUpFinished();
}
if (mSendGoToSleepFinishedNotificationWhenReady) {
mSendGoToSleepFinishedNotificationWhenReady = false;
mNotifier.onGoToSleepFinished();
}
}
/**
* Updates the value of mIsPowered.
* Sets DIRTY_IS_POWERED if a change occurred.
*/
private void updateIsPoweredLocked(int dirty) {
if ((dirty & DIRTY_BATTERY_STATE) != 0) {
final boolean wasPowered = mIsPowered;
final int oldPlugType = mPlugType;
mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
mPlugType = mBatteryService.getPlugType();
mBatteryLevel = mBatteryService.getBatteryLevel();
if (DEBUG) {
Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
+ ", mIsPowered=" + mIsPowered
+ ", oldPlugType=" + oldPlugType
+ ", mPlugType=" + mPlugType
+ ", mBatteryLevel=" + mBatteryLevel);
}
if (wasPowered != mIsPowered || oldPlugType != mPlugType) {
mDirty |= DIRTY_IS_POWERED;
// Treat plugging and unplugging the devices as a user activity.
// Users find it disconcerting when they plug or unplug the device
// and it shuts off right away.
// Some devices also wake the device when plugged or unplugged because
// they don't have a charging LED.
final long now = SystemClock.uptimeMillis();
if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType)) {
wakeUpNoUpdateLocked(now);
}
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
// Tell the notifier whether wireless charging has started so that
// it can provide feedback to the user. Refer to
// shouldWakeUpWhenPluggedOrUnpluggedLocked for justification of the
// heuristics used here.
if (!wasPowered && mIsPowered
&& mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS
&& mBatteryLevel < WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT) {
mNotifier.onWirelessChargingStarted();
}
}
}
}
private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(boolean wasPowered, int oldPlugType) {
// Don't wake when powered unless configured to do so.
if (!mWakeUpWhenPluggedOrUnpluggedConfig) {
return false;
}
// FIXME: Need more accurate detection of wireless chargers.
//
// We are unable to accurately detect whether the device is resting on the
// charger unless it is actually receiving power. This causes us some grief
// because the device might not appear to be plugged into the wireless charger
// unless it actually charging.
//
// To avoid spuriously waking the screen, we apply a special policy to
// wireless chargers.
//
// 1. Don't wake the device when unplugged from wireless charger because
// it might be that the device is still resting on the wireless charger
// but is not receiving power anymore because the battery is full.
//
// 2. Don't wake the device when plugged into a wireless charger if the
// battery already appears to be mostly full. This situation may indicate
// that the device was resting on the charger the whole time and simply
// wasn't receiving power because the battery was full. We can't tell
// whether the device was just placed on the charger or whether it has
// been there for half of the night slowly discharging until it hit
// the point where it needed to start charging again.
if (wasPowered && !mIsPowered
&& oldPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
return false;
}
if (!wasPowered && mIsPowered
&& mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS
&& mBatteryLevel >= WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT) {
return false;
}
// If already dreaming and becoming powered, then don't wake.
if (mIsPowered && (mWakefulness == WAKEFULNESS_NAPPING
|| mWakefulness == WAKEFULNESS_DREAMING)) {
return false;
}
// Otherwise wake up!
return true;
}
/**
* Updates the value of mStayOn.
* Sets DIRTY_STAY_ON if a change occurred.
*/
private void updateStayOnLocked(int dirty) {
if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) {
final boolean wasStayOn = mStayOn;
if (mStayOnWhilePluggedInSetting != 0
&& !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting);
} else {
mStayOn = false;
}
if (mStayOn != wasStayOn) {
mDirty |= DIRTY_STAY_ON;
}
}
}
/**
* Updates the value of mWakeLockSummary to summarize the state of all active wake locks.
* Note that most wake-locks are ignored when the system is asleep.
*
* This function must have no other side-effects.
*/
private void updateWakeLockSummaryLocked(int dirty) {
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {
mWakeLockSummary = 0;
final int numWakeLocks = mWakeLocks.size();
for (int i = 0; i < numWakeLocks; i++) {
final WakeLock wakeLock = mWakeLocks.get(i);
switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.PARTIAL_WAKE_LOCK:
mWakeLockSummary |= WAKE_LOCK_CPU;
break;
case PowerManager.FULL_WAKE_LOCK:
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU
| WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
if (mWakefulness == WAKEFULNESS_AWAKE) {
mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
}
}
break;
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT;
if (mWakefulness == WAKEFULNESS_AWAKE) {
mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
}
}
break;
case PowerManager.SCREEN_DIM_WAKE_LOCK:
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM;
if (mWakefulness == WAKEFULNESS_AWAKE) {
mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
}
}
break;
case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_PROXIMITY_SCREEN_OFF;
}
break;
}
}
if (DEBUG_SPEW) {
Slog.d(TAG, "updateWakeLockSummaryLocked: mWakefulness="
+ wakefulnessToString(mWakefulness)
+ ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
}
}
}
/**
* Updates the value of mUserActivitySummary to summarize the user requested
* state of the system such as whether the screen should be bright or dim.
* Note that user activity is ignored when the system is asleep.
*
* This function must have no other side-effects.
*/
private void updateUserActivitySummaryLocked(long now, int dirty) {
// Update the status of the user activity timeout timer.
if ((dirty & (DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
long nextTimeout = 0;
if (mWakefulness != WAKEFULNESS_ASLEEP) {
final int screenOffTimeout = getScreenOffTimeoutLocked();
final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
mUserActivitySummary = 0;
if (mLastUserActivityTime >= mLastWakeTime) {
nextTimeout = mLastUserActivityTime
+ screenOffTimeout - screenDimDuration;
if (now < nextTimeout) {
mUserActivitySummary |= USER_ACTIVITY_SCREEN_BRIGHT;
} else {
nextTimeout = mLastUserActivityTime + screenOffTimeout;
if (now < nextTimeout) {
mUserActivitySummary |= USER_ACTIVITY_SCREEN_DIM;
}
}
}
if (mUserActivitySummary == 0
&& mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
if (now < nextTimeout
&& mDisplayPowerRequest.screenState
!= DisplayPowerRequest.SCREEN_STATE_OFF) {
mUserActivitySummary = mDisplayPowerRequest.screenState
== DisplayPowerRequest.SCREEN_STATE_BRIGHT ?
USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM;
}
}
if (mUserActivitySummary != 0) {
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextTimeout);
}
} else {
mUserActivitySummary = 0;
}
if (DEBUG_SPEW) {
Slog.d(TAG, "updateUserActivitySummaryLocked: mWakefulness="
+ wakefulnessToString(mWakefulness)
+ ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
+ ", nextTimeout=" + TimeUtils.formatUptime(nextTimeout));
}
}
}
/**
* Called when a user activity timeout has occurred.
* Simply indicates that something about user activity has changed so that the new
* state can be recomputed when the power state is updated.
*
* This function must have no other side-effects besides setting the dirty
* bit and calling update power state. Wakefulness transitions are handled elsewhere.
*/
private void handleUserActivityTimeout() { // runs on handler thread
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "handleUserActivityTimeout");
}
mDirty |= DIRTY_USER_ACTIVITY;
updatePowerStateLocked();
}
}
private int getScreenOffTimeoutLocked() {
int timeout = mScreenOffTimeoutSetting;
if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin);
}
if (mUserActivityTimeoutOverrideFromWindowManager >= 0) {
timeout = (int)Math.min(timeout, mUserActivityTimeoutOverrideFromWindowManager);
}
return Math.max(timeout, MINIMUM_SCREEN_OFF_TIMEOUT);
}
private int getScreenDimDurationLocked(int screenOffTimeout) {
return Math.min(SCREEN_DIM_DURATION,
(int)(screenOffTimeout * MAXIMUM_SCREEN_DIM_RATIO));
}
/**
* Updates the wakefulness of the device.
*
* This is the function that decides whether the device should start napping
* based on the current wake locks and user activity state. It may modify mDirty
* if the wakefulness changes.
*
* Returns true if the wakefulness changed and we need to restart power state calculation.
*/
private boolean updateWakefulnessLocked(int dirty) {
boolean changed = false;
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
| DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
| DIRTY_DOCK_STATE)) != 0) {
if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
if (DEBUG_SPEW) {
Slog.d(TAG, "updateWakefulnessLocked: Bed time...");
}
final long time = SystemClock.uptimeMillis();
if (shouldNapAtBedTimeLocked()) {
changed = napNoUpdateLocked(time);
} else {
changed = goToSleepNoUpdateLocked(time,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
}
}
}
return changed;
}
/**
* Returns true if the device should automatically nap and start dreaming when the user
* activity timeout has expired and it's bedtime.
*/
private boolean shouldNapAtBedTimeLocked() {
return mDreamsActivateOnSleepSetting
|| (mDreamsActivateOnDockSetting
&& mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED);
}
/**
* Returns true if the device should go to sleep now.
* Also used when exiting a dream to determine whether we should go back
* to being fully awake or else go to sleep for good.
*/
private boolean isItBedTimeYetLocked() {
return mBootCompleted && !isBeingKeptAwakeLocked();
}
/**
* Returns true if the device is being kept awake by a wake lock, user activity
* or the stay on while powered setting.
*/
private boolean isBeingKeptAwakeLocked() {
return mStayOn
|| mProximityPositive
|| (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0
|| (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
| USER_ACTIVITY_SCREEN_DIM)) != 0;
}
/**
* Determines whether to post a message to the sandman to update the dream state.
*/
private void updateDreamLocked(int dirty) {
if ((dirty & (DIRTY_WAKEFULNESS
| DIRTY_USER_ACTIVITY
| DIRTY_WAKE_LOCKS
| DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS
| DIRTY_IS_POWERED
| DIRTY_STAY_ON
| DIRTY_PROXIMITY_POSITIVE
| DIRTY_BATTERY_STATE)) != 0) {
scheduleSandmanLocked();
}
}
private void scheduleSandmanLocked() {
if (!mSandmanScheduled) {
mSandmanScheduled = true;
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
/**
* Called when the device enters or exits a napping or dreaming state.
*
* We do this asynchronously because we must call out of the power manager to start
* the dream and we don't want to hold our lock while doing so. There is a risk that
* the device will wake or go to sleep in the meantime so we have to handle that case.
*/
private void handleSandman() { // runs on handler thread
// Handle preconditions.
boolean startDreaming = false;
synchronized (mLock) {
mSandmanScheduled = false;
boolean canDream = canDreamLocked();
if (DEBUG_SPEW) {
Slog.d(TAG, "handleSandman: canDream=" + canDream
+ ", mWakefulness=" + wakefulnessToString(mWakefulness));
}
if (canDream && mWakefulness == WAKEFULNESS_NAPPING) {
startDreaming = true;
}
}
// Start dreaming if needed.
// We only control the dream on the handler thread, so we don't need to worry about
// concurrent attempts to start or stop the dream.
boolean isDreaming = false;
if (mDreamManager != null) {
if (startDreaming) {
mDreamManager.startDream();
}
isDreaming = mDreamManager.isDreaming();
}
// Update dream state.
// We might need to stop the dream again if the preconditions changed.
boolean continueDreaming = false;
synchronized (mLock) {
if (isDreaming && canDreamLocked()) {
if (mWakefulness == WAKEFULNESS_NAPPING) {
mWakefulness = WAKEFULNESS_DREAMING;
mDirty |= DIRTY_WAKEFULNESS;
mBatteryLevelWhenDreamStarted = mBatteryLevel;
updatePowerStateLocked();
continueDreaming = true;
} else if (mWakefulness == WAKEFULNESS_DREAMING) {
if (!isBeingKeptAwakeLocked()
&& mBatteryLevel < mBatteryLevelWhenDreamStarted
- DREAM_BATTERY_LEVEL_DRAIN_CUTOFF) {
// If the user activity timeout expired and the battery appears
// to be draining faster than it is charging then stop dreaming
// and go to sleep.
Slog.i(TAG, "Stopping dream because the battery appears to "
+ "be draining faster than it is charging. "
+ "Battery level when dream started: "
+ mBatteryLevelWhenDreamStarted + "%. "
+ "Battery level now: " + mBatteryLevel + "%.");
} else {
continueDreaming = true;
}
}
}
if (!continueDreaming) {
handleDreamFinishedLocked();
}
}
// Stop dreaming if needed.
// It's possible that something else changed to make us need to start the dream again.
// If so, then the power manager will have posted another message to the handler
// to take care of it later.
if (mDreamManager != null) {
if (!continueDreaming) {
mDreamManager.stopDream();
}
}
}
/**
* Returns true if the device is allowed to dream in its current state
* assuming that it is currently napping or dreaming.
*/
private boolean canDreamLocked() {
return mDreamsSupportedConfig
&& mDreamsEnabledSetting
&& mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF
&& mBootCompleted
&& (mIsPowered || isBeingKeptAwakeLocked());
}
/**
* Called when a dream is ending to figure out what to do next.
*/
private void handleDreamFinishedLocked() {
if (mWakefulness == WAKEFULNESS_NAPPING
|| mWakefulness == WAKEFULNESS_DREAMING) {
if (isItBedTimeYetLocked()) {
goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
updatePowerStateLocked();
} else {
wakeUpNoUpdateLocked(SystemClock.uptimeMillis());
updatePowerStateLocked();
}
}
}
private void handleScreenOnBlockerReleased() {
synchronized (mLock) {
mDirty |= DIRTY_SCREEN_ON_BLOCKER_RELEASED;
updatePowerStateLocked();
}
}
/**
* Updates the display power state asynchronously.
* When the update is finished, mDisplayReady will be set to true. The display
* controller posts a message to tell us when the actual display power state
* has been updated so we come back here to double-check and finish up.
*
* This function recalculates the display power state each time.
*/
private void updateDisplayPowerStateLocked(int dirty) {
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS | DIRTY_SCREEN_ON_BLOCKER_RELEASED)) != 0) {
int newScreenState = getDesiredScreenPowerStateLocked();
if (newScreenState != mDisplayPowerRequest.screenState) {
if (newScreenState == DisplayPowerRequest.SCREEN_STATE_OFF
&& mDisplayPowerRequest.screenState
!= DisplayPowerRequest.SCREEN_STATE_OFF) {
mLastScreenOffEventElapsedRealTime = SystemClock.elapsedRealtime();
}
mDisplayPowerRequest.screenState = newScreenState;
nativeSetPowerState(
newScreenState != DisplayPowerRequest.SCREEN_STATE_OFF,
newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT);
}
int screenBrightness = mScreenBrightnessSettingDefault;
float screenAutoBrightnessAdjustment = 0.0f;
boolean autoBrightness = (mScreenBrightnessModeSetting ==
Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
screenBrightness = mScreenBrightnessOverrideFromWindowManager;
autoBrightness = false;
} else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) {
screenBrightness = mTemporaryScreenBrightnessSettingOverride;
} else if (isValidBrightness(mScreenBrightnessSetting)) {
screenBrightness = mScreenBrightnessSetting;
}
if (autoBrightness) {
screenBrightness = mScreenBrightnessSettingDefault;
if (isValidAutoBrightnessAdjustment(
mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) {
screenAutoBrightnessAdjustment =
mTemporaryScreenAutoBrightnessAdjustmentSettingOverride;
} else if (isValidAutoBrightnessAdjustment(
mScreenAutoBrightnessAdjustmentSetting)) {
screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting;
}
}
screenBrightness = Math.max(Math.min(screenBrightness,
mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum);
screenAutoBrightnessAdjustment = Math.max(Math.min(
screenAutoBrightnessAdjustment, 1.0f), -1.0f);
mDisplayPowerRequest.screenBrightness = screenBrightness;
mDisplayPowerRequest.screenAutoBrightnessAdjustment =
screenAutoBrightnessAdjustment;
mDisplayPowerRequest.useAutoBrightness = autoBrightness;
mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
mDisplayPowerRequest.blockScreenOn = mScreenOnBlocker.isHeld();
mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest,
mRequestWaitForNegativeProximity);
mRequestWaitForNegativeProximity = false;
if (DEBUG_SPEW) {
Slog.d(TAG, "updateScreenStateLocked: mDisplayReady=" + mDisplayReady
+ ", newScreenState=" + newScreenState
+ ", mWakefulness=" + mWakefulness
+ ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
+ ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
+ ", mBootCompleted=" + mBootCompleted);
}
}
}
private static boolean isValidBrightness(int value) {
return value >= 0 && value <= 255;
}
private static boolean isValidAutoBrightnessAdjustment(float value) {
// Handles NaN by always returning false.
return value >= -1.0f && value <= 1.0f;
}
private int getDesiredScreenPowerStateLocked() {
if (mWakefulness == WAKEFULNESS_ASLEEP) {
return DisplayPowerRequest.SCREEN_STATE_OFF;
}
if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
|| (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
|| !mBootCompleted) {
return DisplayPowerRequest.SCREEN_STATE_BRIGHT;
}
return DisplayPowerRequest.SCREEN_STATE_DIM;
}
private final DisplayPowerController.Callbacks mDisplayPowerControllerCallbacks =
new DisplayPowerController.Callbacks() {
@Override
public void onStateChanged() {
mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED;
updatePowerStateLocked();
}
@Override
public void onProximityPositive() {
mProximityPositive = true;
mDirty |= DIRTY_PROXIMITY_POSITIVE;
updatePowerStateLocked();
}
@Override
public void onProximityNegative() {
mProximityPositive = false;
mDirty |= DIRTY_PROXIMITY_POSITIVE;
userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
}
};
private boolean shouldUseProximitySensorLocked() {
return (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0;
}
/**
* Updates the suspend blocker that keeps the CPU alive.
*
* This function must have no other side-effects.
*/
private void updateSuspendBlockerLocked() {
boolean wantCpu = isCpuNeededLocked();
if (wantCpu != mHoldingWakeLockSuspendBlocker) {
mHoldingWakeLockSuspendBlocker = wantCpu;
if (wantCpu) {
if (DEBUG) {
Slog.d(TAG, "updateSuspendBlockerLocked: Acquiring suspend blocker.");
}
mWakeLockSuspendBlocker.acquire();
} else {
if (DEBUG) {
Slog.d(TAG, "updateSuspendBlockerLocked: Releasing suspend blocker.");
}
mWakeLockSuspendBlocker.release();
}
}
}
private boolean isCpuNeededLocked() {
return !mBootCompleted
|| mWakeLockSummary != 0
|| mUserActivitySummary != 0
|| mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF
|| !mDisplayReady;
}
@Override // Binder call
public boolean isScreenOn() {
final long ident = Binder.clearCallingIdentity();
try {
return isScreenOnInternal();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private boolean isScreenOnInternal() {
synchronized (mLock) {
return !mSystemReady
|| mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF;
}
}
private void handleBatteryStateChangedLocked() {
mDirty |= DIRTY_BATTERY_STATE;
updatePowerStateLocked();
}
private void startWatchingForBootAnimationFinished() {
mHandler.sendEmptyMessage(MSG_CHECK_IF_BOOT_ANIMATION_FINISHED);
}
private void checkIfBootAnimationFinished() {
if (DEBUG) {
Slog.d(TAG, "Check if boot animation finished...");
}
if (SystemService.isRunning(BOOT_ANIMATION_SERVICE)) {
mHandler.sendEmptyMessageDelayed(MSG_CHECK_IF_BOOT_ANIMATION_FINISHED,
BOOT_ANIMATION_POLL_INTERVAL);
return;
}
synchronized (mLock) {
if (!mBootCompleted) {
Slog.i(TAG, "Boot animation finished.");
handleBootCompletedLocked();
}
}
}
private void handleBootCompletedLocked() {
final long now = SystemClock.uptimeMillis();
mBootCompleted = true;
mDirty |= DIRTY_BOOT_COMPLETED;
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
}
/**
* Reboots the device.
*
* @param confirm If true, shows a reboot confirmation dialog.
* @param reason The reason for the reboot, or null if none.
* @param wait If true, this call waits for the reboot to complete and does not return.
*/
@Override // Binder call
public void reboot(boolean confirm, String reason, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(false, confirm, reason, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
* Shuts down the device.
*
* @param confirm If true, shows a shutdown confirmation dialog.
* @param wait If true, this call waits for the shutdown to complete and does not return.
*/
@Override // Binder call
public void shutdown(boolean confirm, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(true, confirm, null, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
final String reason, boolean wait) {
if (mHandler == null || !mSystemReady) {
throw new IllegalStateException("Too early to call shutdown() or reboot()");
}
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
if (shutdown) {
ShutdownThread.shutdown(mContext, confirm);
} else {
ShutdownThread.reboot(mContext, reason, confirm);
}
}
}
};
// ShutdownThread must run on a looper capable of displaying the UI.
Message msg = Message.obtain(mHandler, runnable);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
// PowerManager.reboot() is documented not to return so just wait for the inevitable.
if (wait) {
synchronized (runnable) {
while (true) {
try {
runnable.wait();
} catch (InterruptedException e) {
}
}
}
}
}
/**
* Crash the runtime (causing a complete restart of the Android framework).
* Requires REBOOT permission. Mostly for testing. Should not return.
*/
@Override // Binder call
public void crash(String message) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
final long ident = Binder.clearCallingIdentity();
try {
crashInternal(message);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void crashInternal(final String message) {
Thread t = new Thread("PowerManagerService.crash()") {
@Override
public void run() {
throw new RuntimeException(message);
}
};
try {
t.start();
t.join();
} catch (InterruptedException e) {
Log.wtf(TAG, e);
}
}
/**
* Set the setting that determines whether the device stays on when plugged in.
* The argument is a bit string, with each bit specifying a power source that,
* when the device is connected to that source, causes the device to stay on.
* See {@link android.os.BatteryManager} for the list of power sources that
* can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC}
* and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB}
*
* Used by "adb shell svc power stayon ..."
*
* @param val an {@code int} containing the bits that specify which power sources
* should cause the device to stay on.
*/
@Override // Binder call
public void setStayOnSetting(int val) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null);
final long ident = Binder.clearCallingIdentity();
try {
setStayOnSettingInternal(val);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setStayOnSettingInternal(int val) {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.STAY_ON_WHILE_PLUGGED_IN, val);
}
/**
* Used by device administration to set the maximum screen off timeout.
*
* This method must only be called by the device administration policy manager.
*/
@Override // Binder call
public void setMaximumScreenOffTimeoutFromDeviceAdmin(int timeMs) {
final long ident = Binder.clearCallingIdentity();
try {
setMaximumScreenOffTimeoutFromDeviceAdminInternal(timeMs);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) {
synchronized (mLock) {
mMaximumScreenOffTimeoutFromDeviceAdmin = timeMs;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
private boolean isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() {
return mMaximumScreenOffTimeoutFromDeviceAdmin >= 0
&& mMaximumScreenOffTimeoutFromDeviceAdmin < Integer.MAX_VALUE;
}
/**
* Used by the phone application to make the attention LED flash when ringing.
*/
@Override // Binder call
public void setAttentionLight(boolean on, int color) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
setAttentionLightInternal(on, color);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setAttentionLightInternal(boolean on, int color) {
LightsService.Light light;
synchronized (mLock) {
if (!mSystemReady) {
return;
}
light = mAttentionLight;
}
// Control light outside of lock.
light.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
}
/**
* Used by the Watchdog.
*/
public long timeSinceScreenWasLastOn() {
synchronized (mLock) {
if (mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
return 0;
}
return SystemClock.elapsedRealtime() - mLastScreenOffEventElapsedRealTime;
}
}
/**
* Used by the window manager to override the screen brightness based on the
* current foreground activity.
*
* This method must only be called by the window manager.
*
* @param brightness The overridden brightness, or -1 to disable the override.
*/
public void setScreenBrightnessOverrideFromWindowManager(int brightness) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
setScreenBrightnessOverrideFromWindowManagerInternal(brightness);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) {
synchronized (mLock) {
if (mScreenBrightnessOverrideFromWindowManager != brightness) {
mScreenBrightnessOverrideFromWindowManager = brightness;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
}
/**
* Used by the window manager to override the button brightness based on the
* current foreground activity.
*
* This method must only be called by the window manager.
*
* @param brightness The overridden brightness, or -1 to disable the override.
*/
public void setButtonBrightnessOverrideFromWindowManager(int brightness) {
// Do nothing.
// Button lights are not currently supported in the new implementation.
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
}
/**
* Used by the window manager to override the user activity timeout based on the
* current foreground activity. It can only be used to make the timeout shorter
* than usual, not longer.
*
* This method must only be called by the window manager.
*
* @param timeoutMillis The overridden timeout, or -1 to disable the override.
*/
public void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
setUserActivityTimeoutOverrideFromWindowManagerInternal(timeoutMillis);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setUserActivityTimeoutOverrideFromWindowManagerInternal(long timeoutMillis) {
synchronized (mLock) {
if (mUserActivityTimeoutOverrideFromWindowManager != timeoutMillis) {
mUserActivityTimeoutOverrideFromWindowManager = timeoutMillis;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
}
/**
* Used by the settings application and brightness control widgets to
* temporarily override the current screen brightness setting so that the
* user can observe the effect of an intended settings change without applying
* it immediately.
*
* The override will be canceled when the setting value is next updated.
*
* @param brightness The overridden brightness.
*
* @see Settings.System#SCREEN_BRIGHTNESS
*/
@Override // Binder call
public void setTemporaryScreenBrightnessSettingOverride(int brightness) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
setTemporaryScreenBrightnessSettingOverrideInternal(brightness);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setTemporaryScreenBrightnessSettingOverrideInternal(int brightness) {
synchronized (mLock) {
if (mTemporaryScreenBrightnessSettingOverride != brightness) {
mTemporaryScreenBrightnessSettingOverride = brightness;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
}
/**
* Used by the settings application and brightness control widgets to
* temporarily override the current screen auto-brightness adjustment setting so that the
* user can observe the effect of an intended settings change without applying
* it immediately.
*
* The override will be canceled when the setting value is next updated.
*
* @param adj The overridden brightness, or Float.NaN to disable the override.
*
* @see Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ
*/
@Override // Binder call
public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(adj);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(float adj) {
synchronized (mLock) {
// Note: This condition handles NaN because NaN is not equal to any other
// value, including itself.
if (mTemporaryScreenAutoBrightnessAdjustmentSettingOverride != adj) {
mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = adj;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
}
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
*/
public static void lowLevelShutdown() {
nativeShutdown();
}
/**
* Low-level function to reboot the device.
*
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
* @throws IOException if reboot fails for some reason (eg, lack of
* permission)
*/
public static void lowLevelReboot(String reason) throws IOException {
nativeReboot(reason);
}
@Override // Watchdog.Monitor implementation
public void monitor() {
// Grab and release lock for watchdog monitor to detect deadlocks.
synchronized (mLock) {
}
}
@Override // Binder call
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump PowerManager from from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
}
pw.println("POWER MANAGER (dumpsys power)\n");
final DisplayPowerController dpc;
synchronized (mLock) {
pw.println("Power Manager State:");
pw.println(" mDirty=0x" + Integer.toHexString(mDirty));
pw.println(" mWakefulness=" + wakefulnessToString(mWakefulness));
pw.println(" mIsPowered=" + mIsPowered);
pw.println(" mPlugType=" + mPlugType);
pw.println(" mBatteryLevel=" + mBatteryLevel);
pw.println(" mBatteryLevelWhenDreamStarted=" + mBatteryLevelWhenDreamStarted);
pw.println(" mDockState=" + mDockState);
pw.println(" mStayOn=" + mStayOn);
pw.println(" mProximityPositive=" + mProximityPositive);
pw.println(" mBootCompleted=" + mBootCompleted);
pw.println(" mSystemReady=" + mSystemReady);
pw.println(" mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
pw.println(" mSandmanScheduled=" + mSandmanScheduled);
pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime));
pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime));
pw.println(" mSendWakeUpFinishedNotificationWhenReady="
+ mSendWakeUpFinishedNotificationWhenReady);
pw.println(" mSendGoToSleepFinishedNotificationWhenReady="
+ mSendGoToSleepFinishedNotificationWhenReady);
pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime));
pw.println(" mLastUserActivityTimeNoChangeLights="
+ TimeUtils.formatUptime(mLastUserActivityTimeNoChangeLights));
pw.println(" mDisplayReady=" + mDisplayReady);
pw.println(" mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker);
pw.println();
pw.println("Settings and Configuration:");
pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig);
pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting);
pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting);
pw.println(" mMaximumScreenOffTimeoutFromDeviceAdmin="
+ mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced="
+ isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")");
pw.println(" mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting);
pw.println(" mScreenBrightnessSetting=" + mScreenBrightnessSetting);
pw.println(" mScreenAutoBrightnessAdjustmentSetting="
+ mScreenAutoBrightnessAdjustmentSetting);
pw.println(" mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting);
pw.println(" mScreenBrightnessOverrideFromWindowManager="
+ mScreenBrightnessOverrideFromWindowManager);
pw.println(" mUserActivityTimeoutOverrideFromWindowManager="
+ mUserActivityTimeoutOverrideFromWindowManager);
pw.println(" mTemporaryScreenBrightnessSettingOverride="
+ mTemporaryScreenBrightnessSettingOverride);
pw.println(" mTemporaryScreenAutoBrightnessAdjustmentSettingOverride="
+ mTemporaryScreenAutoBrightnessAdjustmentSettingOverride);
pw.println(" mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum);
pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
final int screenOffTimeout = getScreenOffTimeoutLocked();
final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
pw.println();
pw.println("Screen off timeout: " + screenOffTimeout + " ms");
pw.println("Screen dim duration: " + screenDimDuration + " ms");
pw.println();
pw.println("Wake Locks: size=" + mWakeLocks.size());
for (WakeLock wl : mWakeLocks) {
pw.println(" " + wl);
}
pw.println();
pw.println("Suspend Blockers: size=" + mSuspendBlockers.size());
for (SuspendBlocker sb : mSuspendBlockers) {
pw.println(" " + sb);
}
pw.println();
pw.println("Screen On Blocker: " + mScreenOnBlocker);
pw.println();
pw.println("Display Blanker: " + mDisplayBlanker);
dpc = mDisplayPowerController;
}
if (dpc != null) {
dpc.dump(pw);
}
}
private SuspendBlocker createSuspendBlockerLocked(String name) {
SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
mSuspendBlockers.add(suspendBlocker);
return suspendBlocker;
}
private static String wakefulnessToString(int wakefulness) {
switch (wakefulness) {
case WAKEFULNESS_ASLEEP:
return "Asleep";
case WAKEFULNESS_AWAKE:
return "Awake";
case WAKEFULNESS_DREAMING:
return "Dreaming";
case WAKEFULNESS_NAPPING:
return "Napping";
default:
return Integer.toString(wakefulness);
}
}
private static WorkSource copyWorkSource(WorkSource workSource) {
return workSource != null ? new WorkSource(workSource) : null;
}
private final class BatteryReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
handleBatteryStateChangedLocked();
}
}
}
private final class BootCompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// This is our early signal that the system thinks it has finished booting.
// However, the boot animation may still be running for a few more seconds
// since it is ultimately in charge of when it terminates.
// Defer transitioning into the boot completed state until the animation exits.
// We do this so that the screen does not start to dim prematurely before
// the user has actually had a chance to interact with the device.
startWatchingForBootAnimationFinished();
}
}
private final class DreamReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
scheduleSandmanLocked();
}
}
}
private final class UserSwitchedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
handleSettingsChangedLocked();
}
}
}
private final class DockReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
Intent.EXTRA_DOCK_STATE_UNDOCKED);
if (mDockState != dockState) {
mDockState = dockState;
mDirty |= DIRTY_DOCK_STATE;
updatePowerStateLocked();
}
}
}
}
private final class SettingsObserver extends ContentObserver {
public SettingsObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
synchronized (mLock) {
handleSettingsChangedLocked();
}
}
}
/**
* Handler for asynchronous operations performed by the power manager.
*/
private final class PowerManagerHandler extends Handler {
public PowerManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_ACTIVITY_TIMEOUT:
handleUserActivityTimeout();
break;
case MSG_SANDMAN:
handleSandman();
break;
case MSG_SCREEN_ON_BLOCKER_RELEASED:
handleScreenOnBlockerReleased();
break;
case MSG_CHECK_IF_BOOT_ANIMATION_FINISHED:
checkIfBootAnimationFinished();
break;
}
}
}
/**
* Represents a wake lock that has been acquired by an application.
*/
private final class WakeLock implements IBinder.DeathRecipient {
public final IBinder mLock;
public int mFlags;
public String mTag;
public WorkSource mWorkSource;
public int mOwnerUid;
public int mOwnerPid;
public WakeLock(IBinder lock, int flags, String tag, WorkSource workSource,
int ownerUid, int ownerPid) {
mLock = lock;
mFlags = flags;
mTag = tag;
mWorkSource = copyWorkSource(workSource);
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
}
@Override
public void binderDied() {
PowerManagerService.this.handleWakeLockDeath(this);
}
public boolean hasSameProperties(int flags, String tag, WorkSource workSource,
int ownerUid, int ownerPid) {
return mFlags == flags
&& mTag.equals(tag)
&& hasSameWorkSource(workSource)
&& mOwnerUid == ownerUid
&& mOwnerPid == ownerPid;
}
public void updateProperties(int flags, String tag, WorkSource workSource,
int ownerUid, int ownerPid) {
mFlags = flags;
mTag = tag;
updateWorkSource(workSource);
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
}
public boolean hasSameWorkSource(WorkSource workSource) {
return Objects.equal(mWorkSource, workSource);
}
public void updateWorkSource(WorkSource workSource) {
mWorkSource = copyWorkSource(workSource);
}
@Override
public String toString() {
return getLockLevelString()
+ " '" + mTag + "'" + getLockFlagsString()
+ " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")";
}
private String getLockLevelString() {
switch (mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.FULL_WAKE_LOCK:
return "FULL_WAKE_LOCK ";
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
return "SCREEN_BRIGHT_WAKE_LOCK ";
case PowerManager.SCREEN_DIM_WAKE_LOCK:
return "SCREEN_DIM_WAKE_LOCK ";
case PowerManager.PARTIAL_WAKE_LOCK:
return "PARTIAL_WAKE_LOCK ";
case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
return "PROXIMITY_SCREEN_OFF_WAKE_LOCK";
default:
return "??? ";
}
}
private String getLockFlagsString() {
String result = "";
if ((mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
result += " ACQUIRE_CAUSES_WAKEUP";
}
if ((mFlags & PowerManager.ON_AFTER_RELEASE) != 0) {
result += " ON_AFTER_RELEASE";
}
return result;
}
}
private final class SuspendBlockerImpl implements SuspendBlocker {
private final String mName;
private int mReferenceCount;
public SuspendBlockerImpl(String name) {
mName = name;
}
@Override
protected void finalize() throws Throwable {
try {
if (mReferenceCount != 0) {
Log.wtf(TAG, "Suspend blocker \"" + mName
+ "\" was finalized without being released!");
mReferenceCount = 0;
nativeReleaseSuspendBlocker(mName);
}
} finally {
super.finalize();
}
}
@Override
public void acquire() {
synchronized (this) {
mReferenceCount += 1;
if (mReferenceCount == 1) {
nativeAcquireSuspendBlocker(mName);
}
}
}
@Override
public void release() {
synchronized (this) {
mReferenceCount -= 1;
if (mReferenceCount == 0) {
nativeReleaseSuspendBlocker(mName);
} else if (mReferenceCount < 0) {
Log.wtf(TAG, "Suspend blocker \"" + mName
+ "\" was released without being acquired!", new Throwable());
mReferenceCount = 0;
}
}
}
@Override
public String toString() {
synchronized (this) {
return mName + ": ref count=" + mReferenceCount;
}
}
}
private final class ScreenOnBlockerImpl implements ScreenOnBlocker {
private int mNestCount;
public boolean isHeld() {
synchronized (this) {
return mNestCount != 0;
}
}
@Override
public void acquire() {
synchronized (this) {
mNestCount += 1;
if (DEBUG) {
Slog.d(TAG, "Screen on blocked: mNestCount=" + mNestCount);
}
}
}
@Override
public void release() {
synchronized (this) {
mNestCount -= 1;
if (mNestCount < 0) {
Log.wtf(TAG, "Screen on blocker was released without being acquired!",
new Throwable());
mNestCount = 0;
}
if (mNestCount == 0) {
mHandler.sendEmptyMessage(MSG_SCREEN_ON_BLOCKER_RELEASED);
}
if (DEBUG) {
Slog.d(TAG, "Screen on unblocked: mNestCount=" + mNestCount);
}
}
}
@Override
public String toString() {
synchronized (this) {
return "held=" + (mNestCount != 0) + ", mNestCount=" + mNestCount;
}
}
}
private final class DisplayBlankerImpl implements DisplayBlanker {
private boolean mBlanked;
@Override
public void blankAllDisplays() {
synchronized (this) {
mBlanked = true;
mDisplayManagerService.blankAllDisplaysFromPowerManager();
nativeSetInteractive(false);
nativeSetAutoSuspend(true);
}
}
@Override
public void unblankAllDisplays() {
synchronized (this) {
nativeSetAutoSuspend(false);
nativeSetInteractive(true);
mDisplayManagerService.unblankAllDisplaysFromPowerManager();
mBlanked = false;
}
}
@Override
public String toString() {
synchronized (this) {
return "blanked=" + mBlanked;
}
}
}
}
Powered by Gitiles| Privacy| Terms
txt
json
51.反射调用
- 系统休眠:
Class<?> powerManagerClazz = Class.forName("android.os.PowerManager");
Method goToSleep = powerManagerClazz.getMethod("goToSleep",long.class);
goToSleep.invoke(powerManager, SystemClock.uptimeMillis()-2000);
52.系统休眠实践
絮絮叨叨这么多,下面让我们切切实实体验下休眠。
1、休眠模式 休眠是分好几种模式的,不同模式实现方式、耗电量不同,以下来自Documentation/power/states.txt
:
The kernel supports four power management states generically, though
one is generic and the other three are dependent on platform support
code to implement the low-level details for each state.
This file describes each state, what they are
commonly called, what ACPI state they map to, and what string to write
to /sys/power/state to enter that state
state: Freeze / Low-Power Idle
ACPI state: S0
String: "freeze"
This state is a generic, pure software, light-weight, low-power state.
It allows more energy to be saved relative to idle by freezing user
space and putting all I/O devices into low-power states (possibly
lower-power than available at run time), such that the processors can
spend more time in their idle states.
This state can be used for platforms without Standby/Suspend-to-RAM
support, or it can be used in addition to Suspend-to-RAM (memory sleep)
to provide reduced resume latency.
State: Standby / Power-On Suspend
ACPI State: S1
String: "standby"
This state offers minimal, though real, power savings, while providing
a very low-latency transition back to a working system. No operating
state is lost (the CPU retains power), so the system easily starts up
again where it left off.
We try to put devices in a low-power state equivalent to D1, which
also offers low power savings, but low resume latency. Not all devices
support D1, and those that don't are left on.
State: Suspend-to-RAM
ACPI State: S3
String: "mem"
This state offers significant power savings as everything in the
system is put into a low-power state, except for memory, which is
placed in self-refresh mode to retain its contents.
System and device state is saved and kept in memory. All devices are
suspended and put into D3. In many cases, all peripheral buses lose
power when entering STR, so devices must be able to handle the
transition back to the On state.
For at least ACPI, STR requires some minimal boot-strapping code to
resume the system from STR. This may be true on other platforms.
State: Suspend-to-disk
ACPI State: S4
String: "disk"
This state offers the greatest power savings, and can be used even in
the absence of low-level platform support for power management. This
state operates similarly to Suspend-to-RAM, but includes a final step
of writing memory contents to disk. On resume, this is read and memory
is restored to its pre-suspend state.
复制
虽说kernel支持上述四种休眠模式,但具体哪几种可用取决于你的硬件。那么怎么知道自己的Android设备支持的休眠模式呢?
答案:通过/sys/文件系统。查询支持的休眠模式可以cat文件/sys/power/state:
cat /sys/power/state
freeze mem
复制
如果我们往/sys/power/state文件echo上面的某一种模式的字符串,系统就会进入相应的休眠模式:
echo "mem" > /sys/power/state
复制
如果你搜索过Android休眠相关的内容,在老版本的Android(4.4版本之前)会见有提到PowerManager的setPowerState()方法,该方法即是通过以上方式使系统进入休眠。但自从引入Autosleep后,就不在这么做了,setPowerState()方法也销声匿迹。
2、/sys/power/目录下文件
image
文件简介:
- /sys/power/state:用来控制系统的Power状态。读取该文件可以获取系统支持的休眠模式,写入该文件休眠模式的一种,系统进入到指定的休眠模式。如上所示例。
- /sys/power/autosleep:从Android wakelocks补丁集中演化而来,用于取代Android wakelocks中的自动休眠功能。向该文件写入/sys/power/state返回值的某一种,系统会在适当的时候进入指定的休眠的模式;读取该文件返回之前写入的数值。
- /sys/power/wake_lock、/sys/power/wake_unlock:即我们常说的休眠锁,如果应用持有休眠锁,系统将无法进入休眠模式。在Android wakelocks时代,写wake_lock获取锁,写wake_unlock释放锁;在AutoSleep时代,具体参见【Android休眠】之AutoSleep
- wakeup_count:用于解决“system suspend和system wakeup events之间的同步问题”。
- /sys/power/pm_async:状态切换开关,允许/禁止User空间对设备进行异步的suspend和resume操作。
- /sys/power/pm_freeze_timeout:系统在执行休眠动作的时候要冻结(freeze)用户控件的进程和内核空间的允许冻结的内核线程,执行这些操作要耗时间吧?该文件指定所需时间的最大值。
53.EditInput限制输入
为了实现您的需求,您需要使用自定义的InputFilter
来限制EditText
中的输入。下面是如何为EditText
实现自定义的InputFilter
来限制输入为8位字母和数字,以及15位数字和“.”,同时确保只允许输入小写字母和数字:
- 自定义InputFilter:
首先,您需要创建一个自定义的InputFilter
类。这个类将检查输入的字符是否符合您的要求。
public class CustomInputFilter implements InputFilter {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (!Character.isLetterOrDigit(source.charAt(i))) {
return "";
}
}
return source;
}
}
- 在您的布局文件中使用这个过滤器:
在您的布局XML中,将android:digits
属性设置为空,并使用android:inputType="text"
, 然后将自定义的InputFilter
应用到EditText
。
<EditText
android:id="@+id/et_input_ip"
android:layout_width="0dp"
android:layout_height="@dimen/height_48dp"
android:background="@drawable/device_guide_edit_selecter"
app:layout_constraintTop_toBottomOf="@+id/tv_connet"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/cl_connect_bt"
android:layout_marginTop="@dimen/margin_20dp"
android:drawableLeft="@mipmap/icon_search"
android:hint="@string/guide_edit_hint"
android:ellipsize="end"
android:digits="" <!-- 移除digits属性 -->
android:inputType="text" <!-- 设置输入类型为文本 -->
android:singleLine="true"
android:textCursorDrawable="@drawable/color_corsor"
android:textSize="@dimen/size_17sp"
android:paddingLeft="@dimen/padding_12dp"
android:paddingRight="@dimen/dimen_12dp"
android:maxLength="13"
android:textColor="@color/black"
android:drawablePadding="4dp"
android:textColorHint="@color/eshare_993C3C43"
android:layout_marginRight="@dimen/margin_16dp"
android:layout_marginLeft="@dimen/margin_24dp"/>
- 在Activity或Fragment中设置InputFilter:
在您的Activity或Fragment中,获取这个EditText
实例,并设置这个自定义的InputFilter
。
EditText etInputIp = findViewById(R.id.et_input_ip);
etInputIp.setFilters(new InputFilter[]{new CustomInputFilter()}); // 应用过滤器
etPinCode.setFilters(new InputFilter[]{new InputFilter.AllCaps()}); //全部大写字母
etPinCode.addTextChangedListener(new MaxLengthWatcher(9, etPinCode)); //最多输入9位
etPinCode.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
通过上述步骤,您可以确保用户只能输入8位字母和数字,以及15位数字和“.”,并且只能输入小写字母和数字。
54.ANR查看日志
public void onCreate() {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
super.onCreate();
}
adb bugreport bugreport.zip
55378056
55.Ascll码
56.pin码转IP
- 数字转IP
public String eightPinToIp(String connectCode) {
int temp_number = 99999999 - Integer.valueOf(connectCode);
temp_number = temp_number ^ 57395 ;
int ip2 = (temp_number %(256*256*256))/(256*256);
int ip3 = (temp_number%(256*256))/256;
int ip4 = temp_number%256;
Log.d("SHY","ip2:"+ip2+":"+"ip3:"+ip3+":"+"ip4:"+ip4);
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int IP = wifiInfo.getIpAddress();
Log.d("SHY","ipaddress:"+IP);
Log.d("SHY","ipOne:"+(IP & 0xFF)+"==="+"ipTwo:"+((IP >> 8) & 0xFF));
if((IP & 0xFF)==172) //当前wifi连了热点
return String.format(IP_FORMAT, (IP & 0xFF) ,((IP >> 8) & 0xFF),((IP >> 16) & 0xFF),1);
return String.format(IP_FORMAT, IP & 0xFF,ip2, ip3, ip4);
}
- 字母转IP
public static String charToIP(String pin) {
ArrayList<String> alphabet = getAlphabet();
ArrayList pinPos = new ArrayList();
String pinCap = pin.toUpperCase();
int count = pinCap.length();
for (int i = 0; i < count; i++) {
char currPinChar = pinCap.charAt(i);
String pinChar = String.valueOf(currPinChar);
int pos = alphabet.indexOf(pinChar);
if(pos == -1){
return "error";
}
pinPos.add(pos);
}
long firstNum = 0;
int pinCount = pinPos.size();
for (int j = 0; j < pinCount; j++) {
int currentValue = (int)pinPos.get(j);
firstNum += (currentValue * get26Power(pinCount - j));
}
long secondNum = MAX_CHAR_COMB - 1 - firstNum;
long thirdNum = secondNum % MAX_IP_COMB;
long ip1 = thirdNum / (256 * 256 * 256);
long ip2 = (thirdNum % (256 * 256 * 256)) / (256 * 256);
long ip3 = (thirdNum % (256 * 256)) / 256;
long ip4 = thirdNum % 256;
String ip = String.format("%d.%d.%d.%d", ip1, ip2, ip3, ip4);
String language = Locale.getDefault().getLanguage();
if(language.contains("ar")){
ip=ip1+"."+ip2+"."+ip3+"."+ip4;
}
Log.d("SHY","ip:"+ip);
return ip;
}
public static ArrayList<String> getAlphabet() {
ArrayList<String> alphabet = new ArrayList<>();
for (int i = 0; i < 26; i++) {
char aChar = (char)(i + 65);
String alphaChar = String.valueOf(aChar);
alphabet.add(alphaChar);
}
return alphabet;
}
57.EditText输入小写字母转为大写
<EditText
android:id="@+id/et_possword"
android:layout_width="0dp"
android:layout_height="@dimen/height_36dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginRight="@dimen/margin_16dp"
android:layout_marginTop="@dimen/margin_12dp"
android:layout_marginLeft="@dimen/margin_16dp"
android:textCursorDrawable="@drawable/color_corsor"
android:inputType="textPassword"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
app:layout_constraintTop_toBottomOf="@+id/dialog_title"
android:paddingLeft="@dimen/margin_12dp"
android:background="@drawable/tipd_guide_edit_bg"
/>
etPossword.setFilters(arrayOf<InputFilter>(InputFilter.AllCaps()))
58.ESharePro信息
- mac地址:
String mac = Settings.Global.getString(mContext.getContentResolver(), "eshare_mac");
- IP地址
String ipAddress = Settings.Global.getString(mContext.getContentResolver(), SettingsConstants.Key.CURRENT_IP_INFO);
59.获取文件地址
要获取一个ZIP文件的地址,你可以使用Android的ContentResolver类来查询文件的位置。以下是一个示例代码,展示了如何使用ContentResolver来获取ZIP文件的地址:
java
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import java.io.File;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = getIntent();
String action = intent.getAction();
Uri data = intent.getData();
if (Intent.ACTION_VIEW.equals(action) && data != null) {
String scheme = data.getScheme();
if ("content".equals(scheme)) {
ContentResolver contentResolver = getContentResolver();
try {
// 获取文件路径和名称
String fileName = getFileName(data);
// 获取文件所在的目录路径
String directoryPath = getDirectoryPath(data);
// 构造完整的文件路径
File file = new File(directoryPath, fileName);
// 如果文件存在,则返回文件路径,否则返回null
if (file.exists()) {
String filePath = file.getAbsolutePath();
// 在这里可以使用filePath进行后续操作,例如打开文件等
// ...
} else {
// 文件不存在,处理异常情况
// ...
}
} catch (Exception e) {
e.printStackTrace();
}
} else if ("file".equals(scheme)) {
// 处理文件路径直接指定的情况,例如file:///路径/文件名.zip
// ...
} else {
// 处理其他情况,例如未知的URI方案等
// ...
}
} else {
// 处理没有Intent或者数据为空的情况,例如应用启动时的情况等
// ...
}
}
private String getFileName(Uri uri) throws Exception {
// 使用OpenableColumns的DISPLAY_NAME列来获取文件名(如果有的话)
String[] projection = new String[]{MediaStore.MediaColumns.DISPLAY_NAME};
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
return cursor.getString(index);
} else {
return null; // 无法获取文件名信息,返回null或者抛出异常等处理方式根据实际情况而定。
}
}
private String getDirectoryPath(Uri uri) throws Exception {
// 使用OpenableColumns的SIZE列来获取文件所在的目录路径(如果有的话)
String[] projection = new String[]{OpenableColumns.SIZE};
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
return cursor.getString(index);
} else {
return null; // 无法获取文件名信息,返回null或者抛出异常等处理方式根据实际情况而定。
}
}
// OpenableColumns的SIZE列可以用来获取文件所在的目录路径。这个方法不是官方的API,可能会在不同的设备上表现不一致。在某些设备上,它可能会返回null。请谨慎使用。更好的方式是使用FileProvider来指定一个自定义的URI方案。请参考相关文档以获取更多信息。如果无法获取目录路径,可以抛出异常或者返回null等处理方式根据实际情况而定。这里只是一个示例代码,可能需要根据实际情况进行调整和修改。
60.SharedPreferences存储数据
- 存储Bitmap
要将Bitmap对象存储在SharedPreferences中,你需要将其转换为可序列化的格式,例如Base64编码的字符串。以下是一个示例代码片段,演示如何将Bitmap对象存储在SharedPreferences中:
// 获取SharedPreferences对象
SharedPreferences sharedPreferences = context.getSharedPreferences("MyApp", Context.MODE_PRIVATE);
// 创建或获取SharedPreferences.Editor对象
SharedPreferences.Editor editor = sharedPreferences.edit();
// 将Bitmap对象转换为Base64编码的字符串
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); // 将Bitmap压缩为字节数组输出流
byte[] byteArray = baos.toByteArray();
String imageBase64 = Base64.encodeToString(byteArray, Base64.DEFAULT);
// 将Base64编码的字符串保存到SharedPreferences中
editor.putString("image_id", imageBase64);
editor.apply(); // 提交更改
在这个示例中,首先获取了名为"MyApp"的SharedPreferences对象,然后创建或获取一个SharedPreferences.Editor对象。接下来,使用ByteArrayOutputStream将Bitmap对象压缩为字节数组输出流,然后使用Base64编码将字节数组转换为字符串。最后,使用SharedPreferences.Editor的putString方法将Base64编码的字符串保存到SharedPreferences中,并提交更改。
要读取存储在SharedPreferences中的Bitmap对象,你可以使用以下代码:
// 获取SharedPreferences对象
SharedPreferences sharedPreferences = context.getSharedPreferences("MyApp", Context.MODE_PRIVATE);
// 获取Base64编码的字符串
String imageBase64 = sharedPreferences.getString("image_id", null);
// 将Base64编码的字符串转换回Bitmap对象(如果存在)
Bitmap bitmap = null;
if (imageBase64 != null) {
byte[] byteArray = Base64.decode(imageBase64, Base64.DEFAULT);
bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
}
在这个示例中,首先获取名为"MyApp"的SharedPreferences对象,然后使用getString方法获取存储在SharedPreferences中的Base64编码的字符串。如果存在该字符串,则使用Base64解码将其转换回原始的字节数组,并使用BitmapFactory的decodeByteArray方法将字节数组转换回Bitmap对象。
61.多线程同步
- syncronize(lock)
从给出的代码段来看,您想要确保connectAP(ssid, psd);
先执行,然后再根据isConnectAPSuccess
的值决定返回什么。代码中的同步部分确实可以确保connectAP
方法在给定的锁lock
上同步执行。
但是,有一个问题:在同步块内部,您调用了connectAP
方法,但没有等待它的完成。这意味着同步块结束之后,isConnectAPSuccess
的值可能还没有被更新。
为了确保connectAP
方法完全执行并更新isConnectAPSuccess
的值,您可以使用一个标志变量,例如isConnectAPComplete
。这样,您可以检查该标志是否已经设置,以确保connectAP
方法已经完成。
以下是可能的修改:
private volatile boolean isConnectAPComplete = false; // 添加这个标志变量
// ...
isConnectAPSuccess = 1;
synchronized (lock) {
Log.d("LXP", "lock ");
connectAP(ssid, psd); // 假设这个方法有某种方式设置isConnectAPComplete为true
while (!isConnectAPComplete) { // 等待直到connectAP完成并设置标志
lock.wait(); // 如果标志仍未设置,则等待
}
Log.d("LXP", "notifyAll ");
lock.notifyAll();
}
Log.d("LXP", "isConnectAPSuccess " + isConnectAPSuccess);
if (isConnectAPSuccess == 0) {
return 0;
} else if (isConnectAPSuccess == 2) {
return 1;
}
注意:上述代码假设connectAP
方法在完成后会设置isConnectAPComplete
为true
。您需要根据实际的connectAP
方法进行适当的修改。
62.APK反编译
- 反编译命令:进入apk的文件夹路径、路径要绝对路径
apktool d "C:\androidspace\EShareClientV3\app\onescreen\release\OneScreen EShare for Android_v7.4.103.apk"
- 查看apk的包名
apktool if your_apk_file.apk
63.签名
对app-release.apk进行4字节对齐
zipalign -p -f -v 4 .\app-release.apk .\app-release_4.apk
检查签
apksigner verify -v --print-certs .\app-release_4.apk
对app-release_4.apk再签名
apksigner sign --ks .\platform.jks .\app-release_4.apk
64.日志保存
自己写的:
package com.caglobal.kodakluma.util;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class FileLogger {
private static final String LOG_FILE_NAME = "luma-log";
private static final String TAG = "FileLogger";
private static FileLogger mInstance;
private final PrintWriter mWriter;
private FileLogger(){
File logFile = new File(Environment.getExternalStorageDirectory(),LOG_FILE_NAME+System.currentTimeMillis()+".txt");
try {
FileWriter fileWriter = new FileWriter(logFile,true);
mWriter = new PrintWriter(fileWriter);
} catch (IOException e) {
Log.e(TAG,"fail to create log file ",e);
throw new RuntimeException(e);
}
}
public static synchronized FileLogger getInstance(){
if(mInstance == null){
mInstance = new FileLogger();
}
return mInstance;
}
public void log(String message){
if(mWriter != null){
mWriter.println(message);
mWriter.flush();
}else{
Log.e(TAG,"mWriter is null");
}
}
public void close(){
if(mWriter != null){
mWriter.close();
}
}
}
其他人的:
package com.caglobal.kodakluma.util;
import android.graphics.Matrix;
import android.util.Log;
import java.util.Arrays;
/**
* Created by litianji@ee-share.com on 2016/11/2.
*/
public final class LogHelper {
// public static boolean isDebug = BuildConfig.DEBUG;
public static boolean isDebug = false;
private static boolean mSaveLog = true;
private static final String mTag = "miao";
public static final String LOG_FILE_NAME = "%s_%s.txt";
private LogHelper() {
}
public static void V(Object... args) {
println(Log.VERBOSE, args);
}
public static void D(Object... args) {
println(Log.DEBUG, args);
}
public static void I(Object... args) {
println(Log.INFO, args);
}
public static void W(Object... args) {
println(Log.WARN, args);
}
public static void E(Object... args) {
println(Log.ERROR, args);
}
private static void println(int priority, String tag, String msg) {
if (isDebug)
Log.println(priority, tag, msg);
if (mSaveLog) {
String time = CommonUtils.getFormatCurrentTime("yyyyMMdd_HHmmss_SSS");
saveLog(time +" [" + tag + "] " + msg,
String.format(LOG_FILE_NAME, "Log", time.substring(0, 11)), true);
}
}
private static void println(int priority, Object... args) {
if (args == null || args.length == 0) {
return;
}
StringBuilder sb = new StringBuilder();
sb.append(getString(args[0]));
for (int i = 1; i < args.length; i++) {
sb.append(" - ").append(getString(args[i]));
}
println(priority, mTag, sb.toString());
}
private static String getString(Object obj) {
if (obj instanceof Object[]) {
return Arrays.toString((Object[]) obj);
} else if (obj instanceof int[]) {
return Arrays.toString((int[]) obj);
} else if (obj instanceof float[]) {
return Arrays.toString((float[]) obj);
} else if (obj instanceof byte[]) {
return Arrays.toString((byte[]) obj);
} else if (obj instanceof char[]) {
return Arrays.toString((char[]) obj);
} else if (obj instanceof boolean[]) {
return Arrays.toString((boolean[]) obj);
} else if (obj instanceof long[]) {
return Arrays.toString((long[]) obj);
} else if (obj instanceof double[]) {
return Arrays.toString((double[]) obj);
} else if (obj instanceof short[]) {
return Arrays.toString((short[]) obj);
} else if (obj instanceof Throwable) {
return Log.getStackTraceString((Throwable) obj);
}
return String.valueOf(obj);
}
public static void saveLog(String content, String fileName, boolean append) {
FileUtils.saveString(content, fileName, append);
}
public static void printCallers() {
StackTraceElement[] elements = new Throwable().getStackTrace();
for (int i = 1; i < elements.length && i < 11; i++) {
D("---------->" + elements[i].getClassName() + "." + elements[i].getMethodName() + "()");
}
}
public static void printMatrix(Matrix matrix) {
float[] values = new float[9];
matrix.getValues(values);
D("printMatrix", Arrays.toString(values));
}
}
/**
* 保存文本到指定文件中
* @param append 是否需要覆盖之前的文件
*/
public static void saveString(String content, String fileName, boolean append) {
checkFolder(Consts.LOG_SAVE_PATH);
File file = new File(Consts.LOG_SAVE_PATH, fileName);
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(file, append));
writer.write(content);
writer.newLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Environment.getExternalStorageDirectory().getAbsolutePath()用公共路径会出现创建文件失败,考虑使用下面的方法:String path = context.getExternalFilesDir("").getAbsolutePath() + "/Log";
也可以用这个路径:Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
package com.caglobal.kodakluma.util;
import android.content.Context;
import android.graphics.Matrix;
import android.util.Log;
import java.util.Arrays;
/**
* Created by litianji@ee-share.com on 2016/11/2.
*/
public final class LogHelper {
// public static boolean isDebug = BuildConfig.DEBUG;
public static boolean isDebug = false;
private static boolean mSaveLog = true;
private static final String mTag = "miao";
public static final String LOG_FILE_NAME = "%s_%s.txt";
private static Context context;
public LogHelper(Context mcontext) {
context = mcontext;
}
public static void V(Object... args) {
println(Log.VERBOSE, args);
}
public static void D(Object... args) {
println(Log.DEBUG, args);
}
public static void I(Object... args) {
println(Log.INFO, args);
}
public static void W(Object... args) {
println(Log.WARN, args);
}
public static void E(Object... args) {
println(Log.ERROR, args);
}
private static void println(int priority, String tag, String msg) {
Log.d("LXP","println msg:"+msg);
Log.d("LXP","isDebug:"+isDebug+" mSaveLog:"+mSaveLog);
if (isDebug)
Log.println(priority, tag, msg);
if (mSaveLog) {
String time = CommonUtils.getFormatCurrentTime("yyyyMMdd_HHmmss_SSS");
saveLog(time +" [" + tag + "] " + msg,
String.format(LOG_FILE_NAME, "Log", time.substring(0, 11)), true,context);
}
}
private static void println(int priority, Object... args) {
if (args == null || args.length == 0) {
return;
}
StringBuilder sb = new StringBuilder();
sb.append(getString(args[0]));
for (int i = 1; i < args.length; i++) {
sb.append(" - ").append(getString(args[i]));
}
println(priority, mTag, sb.toString());
}
private static String getString(Object obj) {
if (obj instanceof Object[]) {
return Arrays.toString((Object[]) obj);
} else if (obj instanceof int[]) {
return Arrays.toString((int[]) obj);
} else if (obj instanceof float[]) {
return Arrays.toString((float[]) obj);
} else if (obj instanceof byte[]) {
return Arrays.toString((byte[]) obj);
} else if (obj instanceof char[]) {
return Arrays.toString((char[]) obj);
} else if (obj instanceof boolean[]) {
return Arrays.toString((boolean[]) obj);
} else if (obj instanceof long[]) {
return Arrays.toString((long[]) obj);
} else if (obj instanceof double[]) {
return Arrays.toString((double[]) obj);
} else if (obj instanceof short[]) {
return Arrays.toString((short[]) obj);
} else if (obj instanceof Throwable) {
return Log.getStackTraceString((Throwable) obj);
}
return String.valueOf(obj);
}
public static void saveLog(String content, String fileName, boolean append) {
FileUtils.saveString(content, fileName, append);
}
public static void saveLog(String content, String fileName, boolean append, Context context) {
FileUtils.saveString1(content,context);
}
public static void printCallers() {
StackTraceElement[] elements = new Throwable().getStackTrace();
for (int i = 1; i < elements.length && i < 11; i++) {
D("---------->" + elements[i].getClassName() + "." + elements[i].getMethodName() + "()");
}
}
public static void printMatrix(Matrix matrix) {
float[] values = new float[9];
matrix.getValues(values);
D("printMatrix", Arrays.toString(values));
}
}
public static void saveString1(String content,Context context) {
FileOutputStream fos = null;
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd_HH", Locale.getDefault());
Date date = new Date(System.currentTimeMillis());
String time = format.format(date);
String path = context.getExternalFilesDir("").getAbsolutePath() + "/KodakLuma";
Log.d("FileUtils","path:"+path);
File dir = new File(path);
if (!dir.exists())
dir.mkdirs();
File file = new File(path, "/log" + time + ".txt");
if(!file.exists())
Log.d("FileUtils","file not exit...");
try {
fos = new FileOutputStream(path + "/log" + time + ".txt", true);
fos.write(content.getBytes());
fos.write("\n".getBytes()); // 添加一个空行
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
65.自动生成版本号
applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
variant.outputs.all { output ->
if (outputFile != null && outputFileName.endsWith('.apk')) {
outputFileName = "HiShare Client For Android_${defaultConfig.versionName}.apk"
}
}
}
}
static String getVName() {
return "v1.1." + new Date().format("Mdd", TimeZone.getDefault())
}
static String getVCode() {
return new Date().format("yyyMMdd",TimeZone.getDefault()).toInteger()
}
在activity获取版本号
private String getVersionName(Context context) {
String version_name = "";
try {
version_name = context.getPackageManager().getPackageInfo(
context.getPackageName(), 0).versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return version_name;
}
另一种方法:
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
variant.outputs.all { output ->
if (outputFileName.endsWith('.apk')) {
if(flavorName=='hikvision'){
outputFileName = "HIK-shareClient for Android_${defaultConfig.versionName}.apk"
}else if(flavorName=='innovateam'){
outputFileName = "InCast for Android_${defaultConfig.versionName}.apk"
}else if(flavorName=='birdcast'){
outputFileName = "BirdCast for Android_${defaultConfig.versionName}.apk"
}else if(flavorName=='shenxinfu'){
outputFileName = "ShenXinFu for Android_${defaultConfig.versionName}.apk"
}
else{
outputFileName = "Hiby for Android_${defaultConfig.versionName}.apk"
}
}
}
}
}
defaultConfig {
applicationId "com.ecloud.eshare"
minSdkVersion 16
//noinspection OldTargetApi
targetSdkVersion 33
versionCode rootProject.ext.versionCode
// versionName rootProject.ext.versionName
// 动态生成versionName,将第二段版本号设置为2
def baseVersion = "1.2" // 设置基础版本号为1.2
def datePart = generateDatePart() // 生成日期部分
versionName "${baseVersion}.${datePart}"
ndk {
abiFilters 'armeabi', 'arm64-v8a'
// abiFilters 'arm64-v8a'
}
}
buildTypes {
debug {
minifyEnabled false
// 在layout中使用@string/app_version来显示版本
resValue "string", "app_version", "${defaultConfig.versionName}"
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
resValue "string", "app_version", "${defaultConfig.versionName}"
signingConfig signingConfigs.debug
}
}
static def generateDatePart() {
// 这里是一个示例,你需要替换成你自己的日期生成逻辑
return new SimpleDateFormat("Mdd", Locale.getDefault()).format(new Date())
}
66.BeyondCompared4提示“缺少评估信息或损坏”
使用绿色beyond compare4后,在安装的30天后,可能会出现“缺少评估信息或损坏”的提示,解决方案如下。
shell(CMD,powershell或者其他的命令行工具)输入如下:
reg delete "HKEY_CURRENT_USER\Software\Scooter Software\Beyond Compare 4" /v CacheID /f
67.adb安裝apk出錯
- Failure [INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2]
Failure [INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2]
解決:在清單文件的<application>標簽加屬性:android:extractNativeLibs="true"。
68.弱网环境
if(w > h){ //横屏
if(size.width < size.height)
continue;
}else{//竖屏
if(size.width > size.height)
continue;
}
69.隱私規範檢測
https://huaweicloud.csdn.net/64f97de56b896f66024ca100.html
https://github.com/bytedance/appshark/blob/main/doc/zh/overview.md
https://github.com/frida/frida/releases frida
https://huaweicloud.csdn.net/64f9893887b26b6585a1f012.html 赞
& c:/VSCWorkspace/.venv/Scripts/python.exe c:/VSCWorkspace/camille-master/camille.py cast.hiby.com
PS C:\Users\msid> adb root
PS C:\Users\msid> adb shell
Welcome! If you need help getting started, check out our developer FAQ page at:
https://g.126.fm/04jewvw
We're committed to making our emulator as useful as possible for developers,
so if you have any specific requirements or features that you'd like to see
in the emulator, please let us know. We're always open to new ideas and suggestions.
You can find our contact information on the FAQ page as well.
Thanks for using our emulator, happy coding!
2206123SC:/ # cd /data/local/tmp
2206123SC:/data/local/tmp # chmod +x frida-server-16.2.1-android-x86_64
2206123SC:/data/local/tmp # ./frida-server-16.2.1-android-x86_64
mumu模擬器:127.0.0.1:7555
夜神模擬器:127.0.0.1:62001
70.安卓避坑
- 华为mate手机在singleInstancePerTask每次都会重新创建activity,其他手机就不会。
- Dialog在layout文件设置background无效,要在代码中设置。
// 创建ShapeDrawable作为对话框的背景
GradientDrawable drawable = new GradientDrawable();
float cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, context.getResources().getDisplayMetrics());
drawable.setCornerRadius(cornerRadius);
drawable.setColor(Color.parseColor("#ffffff"));
Window window = dialog.getWindow();
if (window != null) {
window.setBackgroundDrawable(drawable);
}
71.问题
-
/system/bin/sh: /frida-server-16.2.1-android-x86_64: inaccessible or not found
chmod +x /frida-server-16.2.1-android-x86_64
-
singleInstancePerTask
啓動投屏的Activity的啓動模式:這個啓動模式不適用與投屏,投屏服務需要在後臺運行,但是使用此模式會導致activity生命周期有影響。太坑了。。。。 需要改成singleTask
72.权限说明弹窗
- 组合弹出:
private void showPrivacyDialog() {
final SelfDialog selfDialog = new SelfDialog(this);
selfDialog.setYesOnclickListener(getString(R.string.tv_nfc_agree), new SelfDialog.onYesOnclickListener() {
@Override
public void onYesClick() {
SpUtil.putBoolean(MainActivity.this,"privacy_key",true);
PlayControl.getInstance().init(MainActivity.this);
privacy_key = SpUtil.getBoolean(MainActivity.this, "privacy_key", false);
selfDialog.dismiss();
Intent intent = getIntent();
LanguageChangeReceiver languageChangeReceiver = new LanguageChangeReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.intent.action.LOCALE_CHANGED");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
registerReceiver(languageChangeReceiver, intentFilter,RECEIVER_EXPORTED);
}else{
registerReceiver(languageChangeReceiver, intentFilter);
}
checkNfcData(intent);
checkIfOpenFileFromThirdApps(intent);
mHandler.sendEmptyMessageDelayed(MSG_SHARE_FILE, 300);
toShowPermissionIns();
}
});
selfDialog.setNoOnclickListener(getString(R.string.tv_nfc_not_use), new SelfDialog.onNoOnclickListener() {
@Override
public void onNoClick() {
// Toast.makeText(MainActivity.this,"点击了--取消--按钮",Toast.LENGTH_LONG).show();
finish();
selfDialog.dismiss();
}
});
selfDialog.show();
}
private void toShowPermissionIns() {
String tips = getString(R.string.media_permission_desc)+"\n"+
getString(R.string.camera_permission_desc)+"\n"+
getString(R.string.location_permission_desc);
showPermissionDialog(tips);
}
private void showPermissionDialog(String msg) {
final SelfDialog selfDialog = new SelfDialog(MainActivity.this,msg,1);
selfDialog.setTitle(getString(R.string.permission_instruction));
selfDialog.setYesOnclickListener(getString(R.string.tv_nfc_agree), new SelfDialog.onYesOnclickListener() {
@Override
public void onYesClick() {
selfDialog.dismiss();
}
});
selfDialog.setNoOnclickListener(getString(R.string.tv_nfc_not_use), new SelfDialog.onNoOnclickListener() {
@Override
public void onNoClick() {
// Toast.makeText(MainActivity.this,"点击了--取消--按钮",Toast.LENGTH_LONG).show();
selfDialog.dismiss();
}
});
selfDialog.show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
privacy_key = SpUtil.getBoolean(this, "privacy_key", false);
if(!privacy_key){
showPrivacyDialog();
}
sInstance = this;
}
- 单个显示:
public void showDialog(Context context, int permissionType, String content) {
Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.permission_dialog);
TextView permission_type = dialog.findViewById(R.id.permission_type);
TextView permission_instr = dialog.findViewById(R.id.permission_instr);
switch (permissionType){
case Constants.CAMERA_TYPE:
permission_type.setText("相机权限使用说明");
permission_instr.setText(context.getResources().getString(R.string.camera_permission_desc));
break;
case Constants.LOCATION_TYPE:
permission_type.setText("位置权限使用说明");
permission_instr.setText(context.getResources().getString(R.string.location_permission_desc));
break;
case Constants.STORAGE_TYPE:
permission_type.setText("媒体文件权限使用说明");
permission_instr.setText(context.getResources().getString(R.string.media_permission_desc));
break;
}
permission_instr.setText(content);
dialog.show();
}
73.相机预览画面
private Camera.Size getBestPreviewSize(int width, int height) {
Camera.Size result = null;
final Camera.Parameters p = mCamera.getParameters();
//特别注意此处需要规定rate的比是大的比小的,不然有可能出现rate = height/width,但是后面遍历的时候,current_rate = width/height,所以我们限定都为大的比小的。
float rate = (float) Math.max(width, height)/ (float)Math.min(width, height);
float tmp_diff;
float min_diff = -1f;
for (Camera.Size size : p.getSupportedPreviewSizes()) {
float current_rate = (float) Math.max(size.width, size.height)/ (float)Math.min(size.width, size.height);
tmp_diff = Math.abs(current_rate-rate);
if( min_diff < 0){
min_diff = tmp_diff ;
result = size;
}
if( tmp_diff < min_diff ){
min_diff = tmp_diff ;
result = size;
}
}
return result;
}
74.JNI