Skip to content

Commit

Permalink
移除日志列表中条目支持横向滚动的特性
Browse files Browse the repository at this point in the history
适配 Android 14 前台 Service 服务类型新特性
  • Loading branch information
getActivity committed Aug 26, 2023
1 parent cb60c13 commit f742274
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 213 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

* 项目地址:[Github](https://github.com/getActivity/Logcat)

* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/Logcat/releases/download/11.5/Logcat.apk)
* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/Logcat/releases/download/11.6/Logcat.apk)

![](picture/demo_code.png)

Expand Down Expand Up @@ -51,7 +51,7 @@ dependencyResolutionManagement {
```groovy
dependencies {
// 日志调试框架:https://github.com/getActivity/Logcat
debugImplementation 'com.github.getActivity:Logcat:11.5'
debugImplementation 'com.github.getActivity:Logcat:11.6'
}
```

Expand Down
23 changes: 13 additions & 10 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 33
compileSdk 34

defaultConfig {
applicationId "com.hjq.logcat.demo"
minSdkVersion 16
targetSdkVersion 33
versionCode 1150
versionName "11.5"
minSdk 16
targetSdk 34
versionCode 1160
versionName "11.6"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

Expand Down Expand Up @@ -42,12 +42,15 @@ android {
}
}

applicationVariants.all { variant ->
applicationVariants.configureEach { variant ->
// apk 输出文件名配置
variant.outputs.all { output ->
variant.outputs.configureEach { output ->
outputFileName = rootProject.getName() + '.apk'
}
}

// 将构建文件统一输出到项目根目录下的 build 文件夹
setBuildDir(new File(rootDir, "build/${path.replaceAll(':', '/')}"))
}

dependencies {
Expand All @@ -62,11 +65,11 @@ dependencies {
implementation 'com.android.support:design:26.0.0'

// 标题栏:https://github.com/getActivity/TitleBar
implementation 'com.github.getActivity:TitleBar:10.3'
implementation 'com.github.getActivity:TitleBar:10.5'

// 吐司框架:https://github.com/getActivity/Toaster
implementation 'com.github.getActivity:Toaster:12.2'
implementation 'com.github.getActivity:Toaster:12.3'

// 内存泄漏捕捉:https://github.com/square/leakcanary
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}
35 changes: 2 additions & 33 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,36 +1,5 @@
buildscript {
repositories {
// 阿里云云效仓库:https://maven.aliyun.com/mvn/guide
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
// 华为开源镜像:https://mirrors.huaweicloud.com/
maven { url 'https://repo.huaweicloud.com/repository/maven/' }
// JitPack 远程仓库:https://jitpack.io
maven { url 'https://jitpack.io' }
mavenCentral()
google()
// noinspection JcenterRepositoryObsolete
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.2'
}
}

allprojects {
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://repo.huaweicloud.com/repository/maven/' }
maven { url 'https://jitpack.io' }
mavenCentral()
google()
// noinspection JcenterRepositoryObsolete
jcenter()
}

// 将构建文件统一输出到项目根目录下的 build 文件夹
setBuildDir(new File(rootDir, "build/${path.replaceAll(':', '/')}"))
plugins {
id 'com.android.application' version '7.1.0' apply false
}

task clean(type: Delete) {
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ zipStoreBase = GRADLE_USER_HOME
zipStorePath = wrapper/dists
distributionBase = GRADLE_USER_HOME
distributionPath = wrapper/dists
distributionUrl = https\://services.gradle.org/distributions/gradle-6.5-all.zip
distributionUrl = https\://services.gradle.org/distributions/gradle-7.4-all.zip
13 changes: 8 additions & 5 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,27 @@ android {
// 资源前缀限制
resourcePrefix "logcat_"

compileSdkVersion 33
compileSdk 34

defaultConfig {
minSdkVersion 16
versionCode 1150
versionName "11.5"
versionCode 1160
versionName "11.6"
}

lintOptions {
abortOnError false
}

android.libraryVariants.all { variant ->
android.libraryVariants.configureEach { variant ->
// aar 输出文件名配置
variant.outputs.all { output ->
variant.outputs.configureEach { output ->
outputFileName = "${rootProject.name}-${android.defaultConfig.versionName}.aar"
}
}

// 将构建文件统一输出到项目根目录下的 build 文件夹
setBuildDir(new File(rootDir, "build/${path.replaceAll(':', '/')}"))
}

afterEvaluate {
Expand Down
16 changes: 15 additions & 1 deletion library/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
<!-- 前台 Service 权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<!-- 前台 Service 特殊用途权限 -->
<!-- java.lang.SecurityException:
Starting FGS with type specialUse callerApp=ProcessRecord
targetSDK=34 requires permissions: all of the permissions allOf=true
[android.permission.FOREGROUND_SERVICE_SPECIAL_USE] -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />

<!-- 读取其他应用日志权限 -->
<uses-permission android:name="android.permission.READ_LOGS" tools:ignore="ProtectedPermissions" />

Expand All @@ -28,7 +35,14 @@
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:windowSoftInputMode="stateHidden" />

<service android:name=".LogcatService" />
<!-- 适配 targetSdk 34:https://developer.android.google.cn/about/versions/14/changes/fgs-types-required?hl=zh-cn -->
<!-- java.lang.RuntimeException:
Unable to start service com.hjq.logcat.LogcatService with Intent:
android.app.MissingForegroundServiceTypeException:
Starting FGS without a type callerApp=ProcessRecord targetSDK=34 -->
<service android:name=".LogcatService" android:foregroundServiceType="specialUse">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="foo"/>
</service>

</application>

Expand Down
143 changes: 3 additions & 140 deletions library/src/main/java/com/hjq/logcat/LogcatAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
Expand All @@ -18,12 +16,9 @@
import android.text.style.UnderlineSpan;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -244,22 +239,9 @@ void setOnItemLongClickListener(@Nullable OnItemLongClickListener listener) {
mItemLongClickListener = listener;
}

@Override
public void onViewDetachedFromWindow(ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
holder.onDetached();
}

@Override
public void onViewRecycled(@NonNull LogcatAdapter.ViewHolder holder) {
super.onViewRecycled(holder);
holder.onRecycled();
}

public class ViewHolder extends RecyclerView.ViewHolder implements
View.OnTouchListener, View.OnClickListener, View.OnLongClickListener {
View.OnClickListener, View.OnLongClickListener {

private final HorizontalScrollView mHorizontalScrollView;
private final TextView mContentView;
private final TextView mIndexView;
private final View mLineView;
Expand All @@ -268,15 +250,12 @@ public class ViewHolder extends RecyclerView.ViewHolder implements
public ViewHolder(View itemView) {
super(itemView);

mHorizontalScrollView = itemView.findViewById(R.id.hcv_log_content);
mContentView = itemView.findViewById(R.id.tv_log_content);
mIndexView = itemView.findViewById(R.id.tv_log_index);
mLineView = itemView.findViewById(R.id.v_log_line);

mHorizontalScrollView.setOnClickListener(this);
mHorizontalScrollView.setOnLongClickListener(this);

mHorizontalScrollView.setOnTouchListener(this);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}

@Override
Expand All @@ -297,80 +276,6 @@ public boolean onLongClick(View v) {
return mItemLongClickListener.onItemLongClick(getItem(layoutPosition), layoutPosition);
}

/** 手指按下的坐标 */
private float mViewDownX;
private float mViewDownY;

/** 手指按下的时间 */
private long mDownTime;

/** 触摸移动标记 */
private boolean mMoveTouch;

/**
* View.OnTouchListener
*/
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录按下的位置(相对 View 的坐标)
mViewDownX = event.getX();
mViewDownY = event.getY();
mDownTime = System.currentTimeMillis();
mMoveTouch = false;
break;
case MotionEvent.ACTION_MOVE:
if (!mMoveTouch && isTouchMove(mViewDownX, event.getX(), mViewDownY, event.getY())) {
// 如果用户移动了手指,那么就拦截本次触摸事件,从而不让点击事件生效
mMoveTouch = true;
}
break;
case MotionEvent.ACTION_UP:
// java.lang.IndexOutOfBoundsException: Index: 813, Size: 801
int layoutPosition = getLayoutPosition();
if (layoutPosition <= getItemCount()) {
LogcatInfo info = getItem(layoutPosition);
int scrollX = mHorizontalScrollView.getScrollX();
mScrollXSet.put(info.hashCode(), scrollX);
}
if (!mMoveTouch) {
if (System.currentTimeMillis() - mDownTime > 200) {
v.performLongClick();
} else {
v.performClick();
}
}
break;
default:
break;
}
return false;
}

/**
* 判断用户是否移动了,判断标准以下:
* 根据手指按下和抬起时的坐标进行判断,不能根据有没有 move 事件来判断
* 因为在有些机型上面,就算用户没有手指没有移动也会产生 move 事件
*
* @param downX 手指按下时的 x 坐标
* @param upX 手指抬起时的 x 坐标
* @param downY 手指按下时的 y 坐标
* @param upY 手指抬起时的 y 坐标
*/
protected boolean isTouchMove(float downX, float upX, float downY, float upY) {
float minTouchSlop = getScaledTouchSlop();
return Math.abs(downX - upX) >= minTouchSlop || Math.abs(downY - upY) >= minTouchSlop;
}

/**
* 获取最小触摸距离
*/
protected float getScaledTouchSlop() {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, Resources.getSystem().getDisplayMetrics());
}

private void onBindView(LogcatInfo info, int position) {
String content = info.toString(mContext);
// suspend all histogram: Sum: 45.480ms 99% C.I. 0.342us-1624.319us Avg: 196.883us Max: 1880us
Expand Down Expand Up @@ -557,49 +462,7 @@ private void onBindView(LogcatInfo info, int position) {
mContentView.setMaxLines(Integer.MAX_VALUE);
mIndexView.setText(String.valueOf(position + 1));
}

mHorizontalScrollView.post(mScrollRunnable);
}

/**
* ViewHolder 解绑时回调
*/
public void onDetached() {
mHorizontalScrollView.removeCallbacks(mScrollRunnable);
}

/**
* ViewHolder 回收时回调
*/
public void onRecycled() {
mHorizontalScrollView.removeCallbacks(mScrollRunnable);
}

/**
* 滚动任务
*/
private final Runnable mScrollRunnable = new Runnable() {

@Override
public void run() {
int layoutPosition = getLayoutPosition();
if (layoutPosition < 0) {
// 在进行屏幕旋转时,位置索引会为 -1
// java.lang.ArrayIndexOutOfBoundsException: length=163; index=-1
return;
}
if (layoutPosition >= getItemCount()) {
// 避免数组出现越界
return;
}
LogcatInfo info = getItem(layoutPosition);
int scrollX = mScrollXSet.get(info.hashCode());
if (mHorizontalScrollView.getScrollX() == scrollX) {
return;
}
mHorizontalScrollView.scrollTo(scrollX, 0);
}
};
}

/**
Expand Down
Loading

0 comments on commit f742274

Please sign in to comment.