├── .gitignore ├── .gitmodules ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── README_CN.md ├── READMORE.md ├── READMORE_CN.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── README.md │ ├── git.png │ ├── git_cmd.pdf │ └── internal │ │ ├── NOTICE │ │ └── beep.ogg │ ├── java │ └── tech │ │ └── linjiang │ │ └── android │ │ └── pandora │ │ ├── MyApp.java │ │ ├── RemoteService.java │ │ ├── db │ │ ├── StoreDatabase.java │ │ ├── dao │ │ │ └── DrinkDao.java │ │ └── entity │ │ │ └── Drink.java │ │ ├── net │ │ └── ApiService.java │ │ ├── ui │ │ ├── MainActivity.java │ │ ├── StackViewActivity.java │ │ └── TransActivity.java │ │ ├── utils │ │ ├── AssetUtil.java │ │ └── ThreadPool.java │ │ └── viewmodel │ │ └── MainViewModel.java │ └── res │ ├── drawable-xxhdpi │ ├── ic_launcher_round.png │ └── ic_quotes.png │ ├── drawable │ └── btn_background.xml │ ├── layout │ ├── activity_main.xml │ ├── activity_ui_stack_view.xml │ ├── activity_ui_test.xml │ └── item_picture.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values-zh │ └── strings.xml │ ├── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── network_security_config.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pandora-core ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── tmp_json.html │ ├── java │ └── tech │ │ └── linjiang │ │ └── pandora │ │ ├── FuncController.java │ │ ├── Pandora.java │ │ ├── cache │ │ ├── CacheDatabase.java │ │ ├── Content.java │ │ ├── Crash.java │ │ ├── History.java │ │ └── Summary.java │ │ ├── crash │ │ └── CrashHandler.java │ │ ├── database │ │ ├── Column.java │ │ ├── DatabaseDescriptor.java │ │ ├── DatabaseDriver.java │ │ ├── DatabaseProvider.java │ │ ├── DatabaseResult.java │ │ ├── Databases.java │ │ └── protocol │ │ │ ├── IDescriptor.java │ │ │ ├── IDriver.java │ │ │ └── IProvider.java │ │ ├── function │ │ └── IFunc.java │ │ ├── history │ │ └── HistoryRecorder.java │ │ ├── inspector │ │ ├── BaseLineView.java │ │ ├── CurInfoView.java │ │ ├── ElementHoldView.java │ │ ├── GridLineView.java │ │ ├── OperableView.java │ │ ├── attribute │ │ │ ├── AttrFactory.java │ │ │ ├── IParser.java │ │ │ └── parser │ │ │ │ ├── ImageViewParser.java │ │ │ │ ├── TextViewParser.java │ │ │ │ ├── ViewGroupParser.java │ │ │ │ └── ViewParser.java │ │ ├── canvas │ │ │ ├── ClickInfoCanvas.java │ │ │ ├── GridCanvas.java │ │ │ ├── RelativeCanvas.java │ │ │ └── SelectCanvas.java │ │ └── model │ │ │ ├── Attribute.java │ │ │ └── Element.java │ │ ├── network │ │ ├── NetStateListener.java │ │ └── OkHttpInterceptor.java │ │ ├── preference │ │ ├── SharedPref.java │ │ ├── SharedPrefDriver.java │ │ ├── SharedPrefProvider.java │ │ └── protocol │ │ │ └── IProvider.java │ │ ├── sandbox │ │ └── Sandbox.java │ │ ├── ui │ │ ├── Dispatcher.java │ │ ├── GeneralDialog.java │ │ ├── TransActivity.java │ │ ├── connector │ │ │ ├── SimpleOnActionExpandListener.java │ │ │ ├── SimpleOnQueryTextListener.java │ │ │ ├── Type.java │ │ │ └── UIStateCallback.java │ │ ├── fragment │ │ │ ├── AddRowFragment.java │ │ │ ├── BaseFragment.java │ │ │ ├── BaseListFragment.java │ │ │ ├── ConfigFragment.java │ │ │ ├── CrashFragment.java │ │ │ ├── CrashStackFragment.java │ │ │ ├── DBFragment.java │ │ │ ├── EditFragment.java │ │ │ ├── FileAttrFragment.java │ │ │ ├── FileFragment.java │ │ │ ├── HierarchyFragment.java │ │ │ ├── HistoryFragment.java │ │ │ ├── MeasureFragment.java │ │ │ ├── NetContentFragment.java │ │ │ ├── NetFragment.java │ │ │ ├── NetSummaryFragment.java │ │ │ ├── PermissionReqFragment.java │ │ │ ├── RouteFragment.java │ │ │ ├── RouteParamFragment.java │ │ │ ├── SPFragment.java │ │ │ ├── SandboxFragment.java │ │ │ ├── TableFragment.java │ │ │ ├── ViewAttrFragment.java │ │ │ └── ViewFragment.java │ │ ├── item │ │ │ ├── CheckBoxItem.java │ │ │ ├── ContentItem.java │ │ │ ├── CrashItem.java │ │ │ ├── DBItem.java │ │ │ ├── ExceptionItem.java │ │ │ ├── FileItem.java │ │ │ ├── FuncItem.java │ │ │ ├── GridItem.java │ │ │ ├── HierarchyItem.java │ │ │ ├── KeyEditItem.java │ │ │ ├── KeyValueItem.java │ │ │ ├── NameArrowItem.java │ │ │ ├── NameItem.java │ │ │ ├── NetItem.java │ │ │ ├── OptionItem.java │ │ │ ├── RouteItem.java │ │ │ ├── RouteParamItem.java │ │ │ ├── SPItem.java │ │ │ ├── TitleItem.java │ │ │ ├── ViewAttrItem.java │ │ │ └── ViewItem.java │ │ ├── recyclerview │ │ │ ├── BaseItem.java │ │ │ ├── GridDividerDecoration.java │ │ │ └── UniversalAdapter.java │ │ └── view │ │ │ ├── ExtraEditTextView.java │ │ │ ├── FuncView.java │ │ │ ├── MenuRecyclerView.java │ │ │ ├── MultiRvLayout.java │ │ │ ├── SquareLayout.java │ │ │ ├── SwipeBackLayout.java │ │ │ └── TreeNodeLayout.java │ │ └── util │ │ ├── Config.java │ │ ├── FileUtil.java │ │ ├── FormatUtil.java │ │ ├── Reflect28Util.java │ │ ├── SensorDetector.java │ │ ├── SimpleTask.java │ │ ├── Utils.java │ │ └── ViewKnife.java │ └── res │ ├── anim │ ├── slide_in_right_.xml │ └── slide_out_right_.xml │ ├── drawable-xxhdpi │ ├── pd_add.png │ ├── pd_bug.png │ ├── pd_close.png │ ├── pd_collapse.png │ ├── pd_config.png │ ├── pd_delete.png │ ├── pd_disk.png │ ├── pd_done.png │ ├── pd_drag.png │ ├── pd_edit.png │ ├── pd_error.png │ ├── pd_expand.png │ ├── pd_grid.png │ ├── pd_help.png │ ├── pd_history.png │ ├── pd_image.png │ ├── pd_info.png │ ├── pd_layer.png │ ├── pd_map.png │ ├── pd_more.png │ ├── pd_network.png │ ├── pd_play.png │ ├── pd_right.png │ ├── pd_ruler.png │ ├── pd_search.png │ ├── pd_select.png │ ├── pd_shadow_left.png │ ├── pd_transform.png │ ├── pd_up_down.png │ └── pd_windows.png │ ├── drawable │ ├── pd_shadow_131124.9.png │ ├── pd_shadow_23354.9.png │ ├── pd_shape_btn_bg.xml │ └── pd_shape_btn_bg_related.xml │ ├── layout │ ├── pd_item_checkbox.xml │ ├── pd_item_common.xml │ ├── pd_item_exception.xml │ ├── pd_item_func.xml │ ├── pd_item_hierachy.xml │ ├── pd_item_key_value.xml │ ├── pd_item_net.xml │ ├── pd_item_option.xml │ ├── pd_item_route.xml │ ├── pd_item_route_param.xml │ ├── pd_item_table_cell.xml │ ├── pd_item_title.xml │ ├── pd_item_view_name.xml │ ├── pd_layout_search_view.xml │ └── pd_layout_view_panel.xml │ ├── values-zh │ └── strings.xml │ ├── values │ ├── colors.xml │ ├── ids.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── provider_path.xml ├── pandora-logo ├── horizontal.png ├── logomark.png └── vertical.png ├── pandora-plugin ├── .gitignore ├── build.gradle └── src │ └── main │ ├── kotlin │ └── tech │ │ └── linjiang │ │ └── pandora │ │ └── gradle │ │ ├── PandoraPlugin.kt │ │ ├── PandoraTransform.kt │ │ ├── file.kt │ │ └── transformers.kt │ └── resources │ └── META-INF │ └── gradle-plugins │ └── pandora-plugin.properties └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | .idea -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "no-op"] 2 | path = no-op 3 | url = https://github.com/whataa/pandora-no-op 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | android: 3 | components: 4 | # Uncomment the lines below if you want to 5 | # use the latest revision of Android SDK Tools 6 | # - platform-tools 7 | # - tools 8 | - tools 9 | - platform-tools 10 | - tools 11 | 12 | # The BuildTools version used by your project 13 | - build-tools-28.0.3 14 | 15 | # The SDK version used to compile your project 16 | - android-28 17 | 18 | # Additional components 19 | - extra-google-m2repository 20 | - extra-android-m2repository 21 | script: 22 | - ./gradlew assembleDebug 23 | 24 | before_deploy: 25 | - mv app/build/outputs/apk/debug/app-debug.apk app/build/outputs/apk/debug/pandora.apk 26 | 27 | deploy: 28 | provider: releases 29 | api_key: 30 | secure: "${GITHUB_AUTH}" 31 | file: app/build/outputs/apk/debug/pandora.apk 32 | skip_cleanup: true 33 | on: 34 | tags: true -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | First of all, thank you for considering to contribute for pandora. The community is important to everyone. In order to build a better tool, please take a moment to read the CONTRIBUTING below. 4 | 5 | ## Submitting a PR 6 | 7 | As an attempt to make the process of contributing more pleasant for you and me, a checklist needs you comfirm: 8 | 9 | - Do not push any tag, branch 10 | - Do not add a new library 11 | - Do not format any submitted code 12 | - Make sure the build passes 13 | 14 | ## Accepting a PR 15 | 16 | - Follow the "Single Activity with multiple Fragments" principle 17 | - Make sure the apis in 「no-op」 are the same with 「core」 18 | - Make sure no unused file/code exists 19 | -------------------------------------------------------------------------------- /READMORE_CN.md: -------------------------------------------------------------------------------- 1 | ## 扩展功能 2 | 3 | ### 1. 添加自定义快捷入口 4 | 5 | 日常开发中,我们可能会在某些页面隐藏一些调试开关,用以“切换开发环境”、“查看Crash日志” 等,如果你有类似需求,可以通过以下方式在Pandora的面板中添加快捷入口: 6 | 1. 实现 `tech.linjiang.pandora.function.IFunc` 接口,返回相应的Icon、Name以及触发操作: 7 | 8 | ``` 9 | private IFunc customFunc = new IFunc() { 10 | @Override 11 | public int getIcon() { 12 | return R.drawable.ic_launcher_round; 13 | } 14 | 15 | @Override 16 | public String getName() { 17 | return getString(R.string.pandora_click_me); 18 | } 19 | 20 | @Override 21 | public boolean onClick() { 22 | toast("I am the custom Function."); 23 | return false; 24 | } 25 | }; 26 | ``` 27 | 28 | 2. 调用 `Pandora.get().addFunc()` 方法传入上述IFunc对象。 29 | 30 | 31 | ### 2. 扩展对查看View属性的支持 32 | 33 | Pandora默认支持动态查看和部分修改View、ViewGroup以及常见的TextView、ImageView控件的属性,如果想查看更多控件的属性,可以通过以下方式进行扩展: 34 | 35 | 1. 实现 `tech.linjiang.pandora.inspector.attribute.IParser` 接口并指定所关注的View类型,这里以已经实现的ImageView为例: 36 | ``` 37 | public class ImageViewParser implements IParser { 38 | 39 | @Override 40 | public List getAttrs(ImageView view) { 41 | List attributes = new ArrayList<>(); 42 | // 添加所关心的属性并返回 43 | Attribute scaleTypeAttribute = new Attribute("scaleType", scaleTypeToStr(view.getScaleType()), Attribute.Edit.SCALE_TYPE); 44 | attributes.add(scaleTypeAttribute); 45 | return attributes; 46 | } 47 | ... 48 | } 49 | ``` 50 | 2. 将新建的Parser添加到Pandora中即可: 51 | ``` 52 | Pandora.get().getAttrFactory().addParser(new ImageViewParser()); 53 | ``` 54 | 在此之后,每次点击查看ImageView控件时,属性列表中会自动将我们所关注的属性值列举出来。 55 | 56 | ### 3. 查看自定义路径的SharedPref文件 57 | 58 | Pandora默认读取的是应用内默认的SP路径下(`data/data//shared_prefs/`)的XML文件,如果有其它非默认路径的SP文件,可以通过以下方式扩展: 59 | 1. 实现 `tech.linjiang.pandora.preference.protocol.IProvider`接口,返回对应的文件列表: 60 | 61 | (具体可参考库中的默认实现`SharedPrefProvider`) 62 | 63 | 2. 将新建的Provider添加到Pandora中即可: 64 | ``` 65 | Pandora.get().getSharedPref().addProvider(new XXProvider()); 66 | ``` 67 | 68 | ## 常见问题 69 | 70 | #### 0. gradle添加依赖失败 71 | 72 | > 1. 请检查是否声明了Jitpack仓库。 73 | > 2. 所有版本号前面有一个`v` 符号,请检查是否遗漏。 74 | 75 | #### 1. 网络日志里没有记录到Header等数据 76 | 77 | > 建议将Pandora的拦截器添加为OKHttp的最后一个。 78 | 79 | #### 2. 不想用摇一摇,和项目有冲突 80 | 81 | > 可以在应用启动时调用 `Pandora.get().disableShakeSwitch();` 方法禁用摇一摇, 82 | 然后在需要的地方调用 `Pandora.get().open();` 手动打开。 83 | 84 | #### 3. 摇一摇没反应,或者很难打开 85 | 86 | > 由于Android机型众多,请手动前往权限中心检查是否授予了「悬浮窗」权限, 87 | > 对于很难打开的情况,可以在「配置」功能里对触发系数进行调整,修改为最适合你手机的值。 88 | 89 | #### 4. 混淆规则 90 | 91 | > 即使建议将Pandora仅用在debug环境,但是无法约束大家在哪种BuildType下开启混淆,因此若有需求请添加以下规则: 92 | 93 | ``` 94 | -keep class tech.linjiang.pandora.**{*;} 95 | ``` 96 | 97 | #### 5. android-support还是AndroidX ? 98 | > 依赖哪种版本取决于你的项目,Pandora提供的两种版本的除了依赖不同,所有逻辑完全一致并保持同步更新; 99 | > 虽然AndroidX是趋势,但是如果你的项目无法迁移到AndroidX还是请使用android-support的方式 100 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion versions.COMPILE_SDK 5 | defaultConfig { 6 | applicationId "tech.linjiang.android.pandora" 7 | minSdkVersion 15 // androidx.browser 8 | targetSdkVersion versions.TARGET_SDK 9 | versionCode 1 10 | versionName "1.0" 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | } 24 | 25 | dependencies { 26 | // migrate to AndroidX : https://blog.csdn.net/qq_17766199/article/details/81433706 27 | implementation fileTree(dir: 'libs', include: ['*.jar']) 28 | implementation project(':pandora-core') 29 | // implementation "com.github.whataa:pandora:v2.0.1" 30 | // 31 | implementation "androidx.appcompat:appcompat:1.0.2" 32 | implementation "androidx.lifecycle:lifecycle-extensions:2.2.0-alpha02" 33 | // 34 | implementation "androidx.room:room-runtime:2.2.0-alpha01" 35 | annotationProcessor "androidx.room:room-compiler:2.2.0-alpha01" 36 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 37 | implementation "androidx.browser:browser:1.0.0" 38 | 39 | implementation "com.squareup.retrofit2:retrofit:2.2.0" 40 | implementation "com.squareup.retrofit2:converter-gson:2.2.0" 41 | implementation 'com.alibaba:fastjson:1.2.45' 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/assets/git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/assets/git.png -------------------------------------------------------------------------------- /app/src/main/assets/git_cmd.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/assets/git_cmd.pdf -------------------------------------------------------------------------------- /app/src/main/assets/internal/beep.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/assets/internal/beep.ogg -------------------------------------------------------------------------------- /app/src/main/java/tech/linjiang/android/pandora/MyApp.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.android.pandora; 2 | 3 | import android.app.Application; 4 | import android.os.Build; 5 | import android.util.Log; 6 | 7 | import tech.linjiang.android.pandora.db.StoreDatabase; 8 | import tech.linjiang.android.pandora.utils.ThreadPool; 9 | 10 | /** 11 | * Created by linjiang on 30/05/2018. 12 | */ 13 | 14 | public class MyApp extends Application { 15 | 16 | private static Application mThis; 17 | 18 | @Override 19 | public void onCreate() { 20 | super.onCreate(); 21 | mThis = this; 22 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 23 | ThreadPool.post(() -> { 24 | // the store.db if exist will be moved to the Device encrypted storage area. 25 | createDeviceProtectedStorageContext().moveDatabaseFrom(mThis, StoreDatabase.NAME); 26 | }); 27 | } 28 | } 29 | 30 | public static Application getContext() { 31 | return mThis; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/tech/linjiang/android/pandora/RemoteService.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.android.pandora; 2 | 3 | import android.app.Service; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.IBinder; 7 | import android.util.Log; 8 | 9 | import java.io.IOException; 10 | import java.net.HttpURLConnection; 11 | import java.net.MalformedURLException; 12 | import java.net.URL; 13 | 14 | import retrofit2.Call; 15 | import retrofit2.Callback; 16 | import retrofit2.Response; 17 | import tech.linjiang.android.pandora.net.ApiService; 18 | import tech.linjiang.android.pandora.utils.ThreadPool; 19 | import tech.linjiang.pandora.Pandora; 20 | 21 | /** 22 | * This service is used to test the behaviors of Pandora in multiple processes 23 | */ 24 | public class RemoteService extends Service { 25 | 26 | public static void start(Context context) { 27 | Intent intent = new Intent(context, RemoteService.class); 28 | context.startService(intent); 29 | } 30 | private static final String TAG = "ProcessService"; 31 | 32 | @Override 33 | public IBinder onBind(Intent intent) { 34 | return null; 35 | } 36 | 37 | @Override 38 | public void onCreate() { 39 | super.onCreate(); 40 | Log.d(TAG, "onCreate: "); 41 | Pandora.get().open(); 42 | } 43 | 44 | @Override 45 | public int onStartCommand(Intent intent, int flags, int startId) { 46 | Log.d(TAG, "onStartCommand: "); 47 | ThreadPool.post(() -> { 48 | try { 49 | URL url = new URL("https://www.v2ex.com/api/topics/latest.json"); 50 | HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 51 | conn.getInputStream(); 52 | } catch (MalformedURLException e) { 53 | e.printStackTrace(); 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | } 57 | }); 58 | ApiService.HttpbinApi api = ApiService.getInstance(); 59 | Callback cb = new Callback() { 60 | @Override 61 | public void onResponse(Call call, Response response) { 62 | } 63 | 64 | @Override 65 | public void onFailure(Call call, Throwable t) { 66 | t.printStackTrace(); 67 | } 68 | }; 69 | api.get().enqueue(cb); 70 | return super.onStartCommand(intent, flags, startId); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/tech/linjiang/android/pandora/db/StoreDatabase.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.android.pandora.db; 2 | 3 | import androidx.sqlite.db.SupportSQLiteDatabase; 4 | import androidx.room.Database; 5 | import androidx.room.Room; 6 | import androidx.room.RoomDatabase; 7 | import androidx.room.migration.Migration; 8 | import androidx.annotation.NonNull; 9 | 10 | import tech.linjiang.android.pandora.MyApp; 11 | import tech.linjiang.android.pandora.db.dao.DrinkDao; 12 | import tech.linjiang.android.pandora.db.entity.Drink; 13 | 14 | 15 | /** 16 | * Created by linjiang on 2018/3/13. 17 | */ 18 | 19 | @Database( 20 | entities = { 21 | Drink.class 22 | }, 23 | version = 4, 24 | exportSchema = false) 25 | public abstract class StoreDatabase extends RoomDatabase { 26 | 27 | public static final String TAG = "StoreDatabase"; 28 | 29 | public static final String NAME = "store.db"; 30 | 31 | public abstract DrinkDao drinkDao(); 32 | 33 | 34 | private static StoreDatabase db = 35 | Room.databaseBuilder(MyApp.getContext(), StoreDatabase.class, NAME) 36 | .allowMainThreadQueries() 37 | .fallbackToDestructiveMigration() 38 | .addMigrations( 39 | new MIGRATE_1_2() 40 | ) 41 | .build(); 42 | 43 | public static StoreDatabase get() { 44 | return db; 45 | } 46 | 47 | private static class MIGRATE_1_2 extends Migration { 48 | 49 | MIGRATE_1_2() { 50 | super(1, 2); 51 | } 52 | 53 | @Override 54 | public void migrate(@NonNull SupportSQLiteDatabase database) { 55 | 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/tech/linjiang/android/pandora/db/dao/DrinkDao.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.android.pandora.db.dao; 2 | 3 | import androidx.room.Dao; 4 | import androidx.room.Insert; 5 | import androidx.room.OnConflictStrategy; 6 | import androidx.room.Query; 7 | import tech.linjiang.android.pandora.db.entity.Drink; 8 | 9 | 10 | /** 11 | * Created by linjiang on 2018/3/13. 12 | * 13 | */ 14 | 15 | @Dao 16 | public interface DrinkDao { 17 | 18 | 19 | @Insert(onConflict = OnConflictStrategy.REPLACE) 20 | long[] insert(Drink... drinks); 21 | 22 | @Query("DELETE FROM Drink") 23 | void delete(); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/tech/linjiang/android/pandora/db/entity/Drink.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.android.pandora.db.entity; 2 | 3 | import androidx.room.ColumnInfo; 4 | import androidx.room.Embedded; 5 | import androidx.room.Entity; 6 | import androidx.room.Ignore; 7 | import androidx.room.PrimaryKey; 8 | 9 | /** 10 | * Created by linjiang on 2018/3/13. 11 | */ 12 | 13 | @Entity 14 | public class Drink { 15 | 16 | @PrimaryKey(autoGenerate = true) 17 | public int id; 18 | 19 | @ColumnInfo(name = "band_name") 20 | public String name; 21 | 22 | public int flavor; 23 | 24 | public String color; 25 | 26 | public int type; 27 | 28 | @Ignore 29 | public int mark; 30 | 31 | @Embedded(prefix = "ingredient_") 32 | public Ingredient ingredient; 33 | 34 | /** 35 | * 成分 36 | */ 37 | public static class Ingredient { 38 | public float water; 39 | public float energy; 40 | public float carbon; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/tech/linjiang/android/pandora/net/ApiService.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.android.pandora.net; 2 | 3 | import okhttp3.OkHttpClient; 4 | import retrofit2.Call; 5 | import retrofit2.Retrofit; 6 | import retrofit2.converter.gson.GsonConverterFactory; 7 | import retrofit2.http.Body; 8 | import retrofit2.http.DELETE; 9 | import retrofit2.http.GET; 10 | import retrofit2.http.Header; 11 | import retrofit2.http.Headers; 12 | import retrofit2.http.PATCH; 13 | import retrofit2.http.POST; 14 | import retrofit2.http.PUT; 15 | import retrofit2.http.Path; 16 | import retrofit2.http.Query; 17 | import tech.linjiang.pandora.Pandora; 18 | 19 | /** 20 | * https://github.com/jgilfelt/chuck 21 | */ 22 | public class ApiService { 23 | 24 | public static HttpbinApi getInstance() { 25 | Retrofit retrofit = new Retrofit.Builder() 26 | .baseUrl("https://httpbin.org") 27 | .client( 28 | new OkHttpClient.Builder() 29 | .addInterceptor(Pandora.get().getInterceptor()) 30 | .build() 31 | ) 32 | .addConverterFactory(GsonConverterFactory.create()) 33 | .build(); 34 | return retrofit.create(HttpbinApi.class); 35 | } 36 | 37 | 38 | 39 | public interface HttpbinApi { 40 | @GET("/get") 41 | Call get(); 42 | @POST("/post") 43 | Call post(@Body Data body); 44 | @PATCH("/patch") 45 | Call patch(@Body Data body); 46 | @PUT("/put") 47 | Call put(@Body Data body); 48 | @DELETE("/delete") 49 | Call delete(); 50 | @GET("/status/{code}") 51 | Call status(@Path("code") int code); 52 | @GET("/stream/{lines}") 53 | Call stream(@Path("lines") int lines); 54 | @GET("/stream-bytes/{bytes}") 55 | Call streamBytes(@Path("bytes") int bytes); 56 | @GET("/delay/{seconds}") 57 | Call delay(@Path("seconds") int seconds); 58 | @GET("/redirect-to") 59 | Call redirectTo(@Query("url") String url); 60 | @GET("/redirect/{times}") 61 | Call redirect(@Path("times") int times); 62 | @GET("/relative-redirect/{times}") 63 | Call redirectRelative(@Path("times") int times); 64 | @GET("/absolute-redirect/{times}") 65 | Call redirectAbsolute(@Path("times") int times); 66 | @GET("/image") 67 | Call image(@Header("Accept") String accept); 68 | @Headers({ 69 | "Accept-Encoding: gzip" 70 | }) 71 | @GET("/gzip") 72 | Call gzip(); 73 | @GET("/xml") 74 | Call xml(); 75 | @GET("/encoding/utf8") 76 | Call utf8(); 77 | @GET("/deflate") 78 | Call deflate(); 79 | @GET("/cookies/set") 80 | Call cookieSet(@Query("k1") String value); 81 | @GET("/basic-auth/{user}/{passwd}") 82 | Call basicAuth(@Path("user") String user, @Path("passwd") String passwd); 83 | @GET("/drip") 84 | Call drip(@Query("numbytes") int bytes, @Query("duration") int seconds, @Query("delay") int delay, @Query("code") int code); 85 | @GET("/deny") 86 | Call deny(); 87 | @GET("/cache") 88 | Call cache(@Header("If-Modified-Since") String ifModifiedSince); 89 | @GET("/cache/{seconds}") 90 | Call cache(@Path("seconds") int seconds); 91 | 92 | @GET("http://imtt.dd.qq.com/16891/5734AD8416A77A38134C21EE3D2E1D01.apk?fsname=com.qqgame.hlddz_6.052.001_196.apk&csr=db5e") 93 | Call download(); 94 | } 95 | 96 | public static class Data { 97 | final String thing; 98 | 99 | public Data(String thing) { 100 | this.thing = thing; 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /app/src/main/java/tech/linjiang/android/pandora/ui/StackViewActivity.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.android.pandora.ui; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.Nullable; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | import tech.linjiang.android.pandora.R; 8 | 9 | /** 10 | * Created by linjiang on 2018/7/14. 11 | */ 12 | 13 | public class StackViewActivity extends AppCompatActivity { 14 | @Override 15 | protected void onCreate(@Nullable Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_ui_stack_view); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/tech/linjiang/android/pandora/ui/TransActivity.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.android.pandora.ui; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.Nullable; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | import tech.linjiang.android.pandora.R; 8 | 9 | /** 10 | * Created by linjiang on 2018/7/14. 11 | */ 12 | 13 | public class TransActivity extends AppCompatActivity { 14 | @Override 15 | protected void onCreate(@Nullable Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | getWindow().getAttributes().alpha = 0.9f; 18 | setContentView(R.layout.activity_ui_test); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/tech/linjiang/android/pandora/utils/AssetUtil.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.android.pandora.utils; 2 | 3 | import android.content.Context; 4 | import android.content.res.AssetManager; 5 | import android.util.Log; 6 | 7 | import java.io.File; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.OutputStream; 12 | 13 | /** 14 | * Created by linjiang on 05/06/2018. 15 | */ 16 | 17 | public class AssetUtil { 18 | 19 | 20 | private static String TARGET_BASE_PATH = ""; 21 | 22 | public static String copyAssertToFiles(Context context) { 23 | TARGET_BASE_PATH = context.getFilesDir().getPath().concat(File.separator); 24 | copyFileOrDir(context, ""); 25 | return TARGET_BASE_PATH; 26 | } 27 | 28 | private static void copyFileOrDir(Context context, String path) { 29 | AssetManager assetManager = context.getAssets(); 30 | String assets[]; 31 | try { 32 | Log.i("tag", "copyFileOrDir() " + path); 33 | assets = assetManager.list(path); 34 | if (assets.length == 0) { 35 | copyFile(context, path); 36 | } else { 37 | String fullPath = TARGET_BASE_PATH + path; 38 | Log.i("tag", "path=" + fullPath); 39 | File dir = new File(fullPath); 40 | if (!dir.exists() && !path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit")) 41 | if (!dir.mkdirs()) 42 | Log.i("tag", "could not create dir " + fullPath); 43 | for (int i = 0; i < assets.length; ++i) { 44 | String p; 45 | if (path.equals("")) 46 | p = ""; 47 | else 48 | p = path + "/"; 49 | 50 | if (!path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit")) 51 | copyFileOrDir(context, p + assets[i]); 52 | } 53 | } 54 | } catch (IOException ex) { 55 | Log.e("tag", "I/O Exception", ex); 56 | } 57 | } 58 | 59 | private static void copyFile(Context context, String filename) { 60 | AssetManager assetManager = context.getAssets(); 61 | 62 | InputStream in; 63 | OutputStream out; 64 | String newFileName = null; 65 | try { 66 | Log.i("tag", "copyFile() " + filename); 67 | in = assetManager.open(filename); 68 | newFileName = TARGET_BASE_PATH + filename; 69 | out = new FileOutputStream(newFileName); 70 | 71 | byte[] buffer = new byte[1024]; 72 | int read; 73 | while ((read = in.read(buffer)) != -1) { 74 | out.write(buffer, 0, read); 75 | } 76 | in.close(); 77 | out.close(); 78 | } catch (Exception e) { 79 | Log.e("tag", "Exception in copyFile() of " + newFileName, e); 80 | } 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/tech/linjiang/android/pandora/utils/ThreadPool.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.android.pandora.utils; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | public class ThreadPool { 7 | 8 | private static final ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); 9 | 10 | public static void post(Runnable task) { 11 | cachedThreadPool.submit(task); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/drawable-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_quotes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/drawable-xxhdpi/ic_quotes.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_ui_stack_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 31 | 32 | 37 | 38 | 43 | 44 | 49 | 50 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_ui_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_picture.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Pandora 3 | Pandora使用悬浮窗来展示功能面板, \n使用前请确保权限已开启。 4 | 摇一摇 5 | 手动打开 6 | 文件下载 7 | 透明Activity 8 | 层叠View 9 | 模拟Crash 10 | 添加自定义Func 11 | 复制Assets到/files/ 12 | 创建新的xml 13 | 创建/重置Db 14 | 创建新文件 15 | 请摇晃手机 16 | 请求已开始,你可以点击「网络日志」查看 17 | 你可以使用「选择视图」功能进行查看 18 | 添加成功,请滑动到面板最右侧查看 19 | 已重置: 20 | 已重置: 21 | 已创建新文件: 22 | 已创建: 23 | 点我试试 24 | 我可以被选中 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | #7f000000 5 | #00897B 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Pandora 3 | 4 | Pandora needs OVERLAY permission to show the function panel,\nbe sure it is allowed before using. 5 | Shake to open 6 | Open 7 | Download File 8 | Trans Activity 9 | Views 10 | Mock Crash 11 | Add Func 12 | Copy assets to /files/ 13 | Create new Xml 14 | Create/Reset Db 15 | Create new File 16 | Please shake your device. 17 | The requests has emitted,you can open「Network」to check. 18 | You can Click「Select」to inspect views. 19 | Add successfully, please swipe to the far right of the panel to check. 20 | Copy: 21 | Reset: 22 | New file: 23 | New xml: 24 | ClickMe 25 | I can be selected 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | kotlin_version = '1.3.61' 6 | } 7 | ext.versions = [ 8 | 'COMPILE_SDK' : 28, 9 | 'MIN_SDK' : 14, 10 | 'TARGET_SDK' : 28, 11 | 'SUPPORT_LIB' : '2.0.0-rc01', 12 | 'OKHTTP' : '4.0.1' 13 | ] 14 | 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | dependencies { 20 | classpath 'com.android.tools.build:gradle:3.3.2' 21 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' 22 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 23 | classpath 'com.novoda:bintray-release:0.9.2' 24 | 25 | // NOTE: Do not place your application dependencies here; they belong 26 | // in the individual module build.gradle files 27 | } 28 | } 29 | 30 | allprojects { 31 | repositories { 32 | google() 33 | jcenter() 34 | maven { url 'https://jitpack.io' } 35 | } 36 | } 37 | 38 | task clean(type: Delete) { 39 | delete rootProject.buildDir 40 | } 41 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | android.enableJetifier=true 13 | android.useAndroidX=true 14 | org.gradle.jvmargs=-Xmx1536m 15 | 16 | # When configured, Gradle will run in incubating parallel mode. 17 | # This option should only be used with decoupled projects. More details, visit 18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 19 | # org.gradle.parallel=true 20 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 14 13:14:49 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /pandora-core/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /pandora-core/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | 4 | group='tech.linjiang' 5 | 6 | android { 7 | compileSdkVersion versions.COMPILE_SDK 8 | defaultConfig { 9 | minSdkVersion versions.MIN_SDK 10 | targetSdkVersion versions.TARGET_SDK 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | consumerProguardFiles 'consumer-rules.pro' 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | api "androidx.appcompat:appcompat:1.0.2" 28 | api "androidx.recyclerview:recyclerview:1.0.0" 29 | api "com.google.android.material:material:1.0.0" 30 | api "com.squareup.okhttp3:okhttp:${versions.OKHTTP}" 31 | } 32 | -------------------------------------------------------------------------------- /pandora-core/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class tech.linjiang.pandora.**{*;} -------------------------------------------------------------------------------- /pandora-core/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /pandora-core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 9 | 11 | 12 | 17 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /pandora-core/src/main/assets/tmp_json.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/cache/Content.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.cache; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | import java.util.List; 6 | 7 | import tech.linjiang.pandora.util.Utils; 8 | 9 | /** 10 | * Created by linjiang on 2018/6/22. 11 | */ 12 | @CacheDatabase.Table("http_content") 13 | public class Content { 14 | 15 | static { 16 | clear(); 17 | } 18 | 19 | @CacheDatabase.Column(value = BaseColumns._ID, primaryKey = true) 20 | public long id; 21 | @CacheDatabase.Column("requestBody") 22 | public String requestBody; 23 | @CacheDatabase.Column("responseBody") 24 | public String responseBody; 25 | 26 | public static Content query(long id) { 27 | List result = CacheDatabase.queryList(Content.class, BaseColumns._ID + " = " + String.valueOf(id), "limit 1"); 28 | if (Utils.isNotEmpty(result)) { 29 | return result.get(0); 30 | } 31 | return null; 32 | } 33 | 34 | public static long insert(Content content) { 35 | return CacheDatabase.insert(content); 36 | } 37 | 38 | public static void update(Content content) { 39 | CacheDatabase.update(content); 40 | } 41 | public static void clear() { 42 | CacheDatabase.delete(Content.class); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/cache/Crash.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.cache; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.os.Build; 7 | import android.provider.BaseColumns; 8 | 9 | import java.io.Serializable; 10 | import java.util.List; 11 | import java.util.Locale; 12 | 13 | import tech.linjiang.pandora.util.Utils; 14 | 15 | /** 16 | * Created by linjiang on 2019/3/3. 17 | */ 18 | 19 | @CacheDatabase.Table("log_crash") 20 | public class Crash implements Serializable { 21 | 22 | 23 | @CacheDatabase.Column(value = BaseColumns._ID, primaryKey = true) 24 | public int id; 25 | 26 | @CacheDatabase.Column("createTime") 27 | public long createTime; 28 | @CacheDatabase.Column("startTime") 29 | public long startTime; 30 | @CacheDatabase.Column("type") 31 | public String type; 32 | @CacheDatabase.Column("cause") 33 | public String cause; 34 | @CacheDatabase.Column("stack") 35 | public String stack; 36 | 37 | @CacheDatabase.Column("versionCode") 38 | public int versionCode; 39 | @CacheDatabase.Column("versionName") 40 | public String versionName; 41 | @CacheDatabase.Column("sys_sdk") 42 | public String systemSDK; 43 | @CacheDatabase.Column("sys_version") 44 | public String systemVersion; 45 | @CacheDatabase.Column("rom") 46 | public String rom; 47 | @CacheDatabase.Column("cpu") 48 | public String cpuABI; 49 | @CacheDatabase.Column("phone") 50 | public String phoneName; 51 | @CacheDatabase.Column("locale") 52 | public String locale; 53 | 54 | 55 | public static void clear() { 56 | CacheDatabase.delete(Crash.class); 57 | } 58 | 59 | public static List query() { 60 | List result = CacheDatabase.queryList(Crash.class, null, "order by createTime DESC"); 61 | return result; 62 | } 63 | 64 | public static void insert(Throwable t, long launchTime) { 65 | Crash crash = new Crash(); 66 | crash.startTime = launchTime; 67 | crash.createTime = System.currentTimeMillis(); 68 | crash.type = t.getClass().getSimpleName(); 69 | crash.cause = t.getMessage(); 70 | crash.stack = Utils.collectThrow(t); 71 | crash.versionCode = packageCode(Utils.getContext()); 72 | crash.versionName = packageName(Utils.getContext()); 73 | crash.systemVersion = Build.VERSION.RELEASE; 74 | crash.systemSDK = "Android " + Build.VERSION.SDK_INT; 75 | crash.rom = Build.MANUFACTURER; 76 | crash.cpuABI = Build.CPU_ABI; 77 | crash.phoneName = Build.MODEL; 78 | crash.locale = Locale.getDefault().getLanguage(); 79 | CacheDatabase.insert(crash); 80 | } 81 | 82 | 83 | private static int packageCode(Context context) { 84 | PackageManager manager = context.getPackageManager(); 85 | int code = 0; 86 | try { 87 | PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); 88 | code = info.versionCode; 89 | } catch (PackageManager.NameNotFoundException e) { 90 | e.printStackTrace(); 91 | } 92 | return code; 93 | } 94 | 95 | private static String packageName(Context context) { 96 | PackageManager manager = context.getPackageManager(); 97 | String name = null; 98 | try { 99 | PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); 100 | name = info.versionName; 101 | } catch (PackageManager.NameNotFoundException e) { 102 | e.printStackTrace(); 103 | } 104 | 105 | return name; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/cache/History.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.cache; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by linjiang on 2019/3/3. 9 | */ 10 | 11 | @CacheDatabase.Table("activity_history") 12 | public class History { 13 | 14 | static { 15 | clear(); 16 | } 17 | 18 | @CacheDatabase.Column(value = BaseColumns._ID, primaryKey = true) 19 | public int id; 20 | @CacheDatabase.Column("createTime") 21 | public long createTime; 22 | @CacheDatabase.Column("activity") 23 | public String activity; 24 | @CacheDatabase.Column("event") 25 | public String event; 26 | 27 | public static void clear() { 28 | CacheDatabase.delete(History.class); 29 | } 30 | 31 | public static void insert(History history) { 32 | CacheDatabase.insert(history); 33 | } 34 | 35 | public static List query() { 36 | String condition = "order by createTime desc"; 37 | return CacheDatabase.queryList(History.class, null, condition); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/cache/Summary.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.cache; 2 | 3 | import android.provider.BaseColumns; 4 | import androidx.annotation.IntDef; 5 | import android.util.Pair; 6 | 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.util.List; 10 | 11 | import tech.linjiang.pandora.util.Config; 12 | import tech.linjiang.pandora.util.Utils; 13 | 14 | /** 15 | * Created by linjiang on 2018/6/22. 16 | */ 17 | 18 | @CacheDatabase.Table("http_summary") 19 | public class Summary { 20 | 21 | static { 22 | clear(); 23 | } 24 | 25 | @CacheDatabase.Column(value = BaseColumns._ID, primaryKey = true) 26 | public long id; 27 | @CacheDatabase.Column("status") 28 | public int status; 29 | @CacheDatabase.Column("code") 30 | public int code; 31 | @CacheDatabase.Column("url") 32 | public String url; 33 | @CacheDatabase.Column("query") 34 | public String query; 35 | @CacheDatabase.Column("host") 36 | public String host; 37 | @CacheDatabase.Column("method") 38 | public String method; 39 | @CacheDatabase.Column("protocol") 40 | public String protocol; 41 | @CacheDatabase.Column("ssl") 42 | public boolean ssl; 43 | @CacheDatabase.Column("start_time") 44 | public long start_time; 45 | @CacheDatabase.Column("end_time") 46 | public long end_time; 47 | @CacheDatabase.Column("request_content_type") 48 | public String request_content_type; 49 | @CacheDatabase.Column("response_content_type") 50 | public String response_content_type; 51 | @CacheDatabase.Column("request_size") 52 | public long request_size; 53 | @CacheDatabase.Column("response_size") 54 | public long response_size; 55 | @CacheDatabase.Column("request_header") 56 | public String requestHeader; 57 | @CacheDatabase.Column("response_header") 58 | public String responseHeader; 59 | 60 | public List> request_header; 61 | public List> response_header; 62 | 63 | 64 | public static List queryList() { 65 | String condition = "order by start_time desc limit " + String.valueOf(Config.getNETWORK_PAGE_SIZE()); 66 | List result = CacheDatabase.queryList(Summary.class, null, condition); 67 | return result; 68 | } 69 | 70 | public static Summary query(long id) { 71 | List result = CacheDatabase.queryList(Summary.class, BaseColumns._ID + " = " + String.valueOf(id), "limit 1"); 72 | if (Utils.isNotEmpty(result)) { 73 | return result.get(0); 74 | } 75 | return null; 76 | } 77 | 78 | public static long insert(Summary summary) { 79 | return CacheDatabase.insert(summary); 80 | } 81 | 82 | public static void update(Summary summary) { 83 | CacheDatabase.update(summary); 84 | } 85 | 86 | public static void clear() { 87 | CacheDatabase.delete(Summary.class); 88 | } 89 | 90 | @IntDef({ 91 | Status.REQUESTING, 92 | Status.ERROR, 93 | Status.COMPLETE 94 | }) 95 | @Retention(RetentionPolicy.SOURCE) 96 | public @interface Status { 97 | int REQUESTING = 0x00; 98 | int ERROR = 0x01; 99 | int COMPLETE = 0x02; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/crash/CrashHandler.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.crash; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | 7 | import tech.linjiang.pandora.cache.Crash; 8 | 9 | /** 10 | * Created by linjiang on 2019/3/1. 11 | */ 12 | 13 | public class CrashHandler implements Thread.UncaughtExceptionHandler { 14 | 15 | private final long launchTime; 16 | private Thread.UncaughtExceptionHandler defHandler; 17 | 18 | public CrashHandler(final Application app) { 19 | launchTime = System.currentTimeMillis(); 20 | app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { 21 | @Override 22 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 23 | /* 24 | Registering here is to prevent other crash-listeners from not callback 25 | in case CrashHandler Registered before Application's onCreate. 26 | */ 27 | app.unregisterActivityLifecycleCallbacks(this); 28 | defHandler = Thread.getDefaultUncaughtExceptionHandler(); 29 | Thread.setDefaultUncaughtExceptionHandler(CrashHandler.this); 30 | } 31 | 32 | @Override public void onActivityStarted(Activity activity) {} 33 | @Override public void onActivityResumed(Activity activity) {} 34 | @Override public void onActivityPaused(Activity activity) {} 35 | @Override public void onActivityStopped(Activity activity) {} 36 | @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} 37 | @Override public void onActivityDestroyed(Activity activity) {} 38 | }); 39 | } 40 | 41 | @Override 42 | public void uncaughtException(Thread t, Throwable e) { 43 | Crash.insert(e, launchTime); 44 | if (defHandler != null) { 45 | defHandler.uncaughtException(t, e); 46 | } 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/database/Column.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.database; 2 | 3 | /** 4 | * Created by linjiang on 06/06/2018. 5 | */ 6 | 7 | public interface Column { 8 | String ROW_ID = "rowId"; 9 | String NAME = "name"; 10 | String TYPE = "type"; 11 | String NOT_NULL = "notnull"; 12 | String DEF_VALUE = "dflt_value"; 13 | String PK = "pk"; 14 | } 15 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/database/DatabaseDescriptor.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.database; 2 | 3 | import java.io.File; 4 | 5 | import tech.linjiang.pandora.database.protocol.IDescriptor; 6 | 7 | /** 8 | * Created by linjiang on 29/05/2018. 9 | */ 10 | 11 | public class DatabaseDescriptor implements IDescriptor { 12 | public final File file; 13 | 14 | public DatabaseDescriptor(File file) { 15 | this.file = file; 16 | } 17 | 18 | @Override 19 | public String name() { 20 | return file.getName(); 21 | } 22 | 23 | @Override 24 | public boolean exist() { 25 | return file.exists(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/database/DatabaseProvider.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.database; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteException; 6 | import android.os.Build; 7 | 8 | import java.io.File; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import tech.linjiang.pandora.database.protocol.IProvider; 13 | 14 | /** 15 | * Created by linjiang on 29/05/2018. 16 | */ 17 | 18 | public class DatabaseProvider implements IProvider { 19 | private Context context; 20 | 21 | DatabaseProvider(Context context) { 22 | this.context = context; 23 | } 24 | 25 | @Override 26 | public List getDatabaseFiles() { 27 | List databaseFiles = new ArrayList<>(); 28 | for (String databaseName : context.databaseList()) { 29 | databaseFiles.add(context.getDatabasePath(databaseName)); 30 | } 31 | return databaseFiles; 32 | } 33 | 34 | @Override 35 | public SQLiteDatabase openDatabase(File databaseFile) throws SQLiteException { 36 | return performOpen(databaseFile, checkIfCanOpenWithWAL(databaseFile)); 37 | } 38 | 39 | private int checkIfCanOpenWithWAL(File databaseFile) { 40 | int flags = 0; 41 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 42 | File walFile = new File(databaseFile.getParent(), databaseFile.getName() + "-wal"); 43 | if (walFile.exists()) { 44 | flags |= SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING; 45 | } 46 | } 47 | return flags; 48 | } 49 | 50 | private SQLiteDatabase performOpen(File databaseFile, int options) { 51 | int flags = SQLiteDatabase.OPEN_READWRITE; 52 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 53 | if ((options & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) { 54 | flags |= SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING; 55 | } 56 | } 57 | SQLiteDatabase db = SQLiteDatabase.openDatabase(databaseFile.getAbsolutePath(), null, flags); 58 | return db; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/database/DatabaseResult.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.database; 2 | 3 | import android.database.Cursor; 4 | import android.database.sqlite.SQLiteException; 5 | 6 | import java.io.UnsupportedEncodingException; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by linjiang on 29/05/2018. 13 | */ 14 | 15 | public class DatabaseResult { 16 | 17 | /** 18 | * Maximum length of a BLOB field before we stop trying to interpret it and just 19 | * return {@link #UNKNOWN_BLOB_LABEL} 20 | */ 21 | private static final int MAX_BLOB_LENGTH = 512; 22 | 23 | /** 24 | * Label to use when a BLOB column cannot be converted to a string. 25 | */ 26 | private static final String UNKNOWN_BLOB_LABEL = "{blob}"; 27 | 28 | public List columnNames; 29 | 30 | public List> values; 31 | 32 | public Error sqlError; 33 | 34 | public static class Error { 35 | public String message; 36 | public int code; 37 | } 38 | 39 | public void transformRawQuery() throws SQLiteException { 40 | } 41 | 42 | public void transformSelect(Cursor result) throws SQLiteException { 43 | columnNames = Arrays.asList(result.getColumnNames()); 44 | values = wrapRows(result); 45 | } 46 | 47 | public void transformInsert(long insertedId) throws SQLiteException { 48 | } 49 | 50 | public void transformUpdateDelete(int count) throws SQLiteException { 51 | } 52 | 53 | 54 | private static List> wrapRows(Cursor cursor) { 55 | List> result = new ArrayList<>(); 56 | 57 | final int numColumns = cursor.getColumnCount(); 58 | while (cursor.moveToNext()) { 59 | ArrayList flatList = new ArrayList<>(); 60 | for (int column = 0; column < numColumns; column++) { 61 | switch (cursor.getType(column)) { 62 | case Cursor.FIELD_TYPE_NULL: 63 | flatList.add(null); 64 | break; 65 | case Cursor.FIELD_TYPE_INTEGER: 66 | flatList.add(String.valueOf(cursor.getLong(column))); 67 | break; 68 | case Cursor.FIELD_TYPE_FLOAT: 69 | flatList.add(String.valueOf(cursor.getDouble(column))); 70 | break; 71 | case Cursor.FIELD_TYPE_BLOB: 72 | flatList.add(blobToString(cursor.getBlob(column))); 73 | break; 74 | case Cursor.FIELD_TYPE_STRING: 75 | default: 76 | flatList.add(cursor.getString(column)); 77 | break; 78 | } 79 | } 80 | result.add(flatList); 81 | } 82 | return result; 83 | } 84 | 85 | private static String blobToString(byte[] blob) { 86 | if (blob.length <= MAX_BLOB_LENGTH) { 87 | if (fastIsAscii(blob)) { 88 | try { 89 | return new String(blob, "US-ASCII"); 90 | } catch (UnsupportedEncodingException ignore) { 91 | } 92 | } 93 | } 94 | return UNKNOWN_BLOB_LABEL; 95 | } 96 | 97 | private static boolean fastIsAscii(byte[] blob) { 98 | for (byte b : blob) { 99 | if ((b & ~0x7f) != 0) { 100 | return false; 101 | } 102 | } 103 | return true; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/database/protocol/IDescriptor.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.database.protocol; 2 | 3 | /** 4 | * Created by linjiang on 29/05/2018. 5 | * 6 | * associated with Driver, can be used to carry some information of the database 7 | */ 8 | 9 | public interface IDescriptor { 10 | String name(); 11 | boolean exist(); 12 | } 13 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/database/protocol/IDriver.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.database.protocol; 2 | 3 | import android.database.sqlite.SQLiteException; 4 | 5 | import java.util.List; 6 | 7 | import tech.linjiang.pandora.database.DatabaseResult; 8 | 9 | /** 10 | * Created by linjiang on 29/05/2018. 11 | * 12 | * Database driver:SQLite、ContentProvider 13 | */ 14 | 15 | public interface IDriver { 16 | List getDatabaseNames(); 17 | 18 | List getTableNames(T databaseDesc) throws SQLiteException; 19 | 20 | void executeSQL(T databaseDesc, String query, DatabaseResult result) throws SQLiteException; 21 | } 22 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/database/protocol/IProvider.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.database.protocol; 2 | 3 | import android.database.sqlite.SQLiteDatabase; 4 | import android.database.sqlite.SQLiteException; 5 | 6 | import java.io.File; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by linjiang on 30/05/2018. 11 | * 12 | * If the database is not in the default path, you can provide the file by implementing the interface 13 | */ 14 | 15 | public interface IProvider { 16 | List getDatabaseFiles(); 17 | SQLiteDatabase openDatabase(File databaseFile) throws SQLiteException; 18 | } 19 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/function/IFunc.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.function; 2 | 3 | import androidx.annotation.DrawableRes; 4 | 5 | /** 6 | * Created by linjiang on 2019/3/4. 7 | *

8 | * Please check @{@link tech.linjiang.pandora.Pandora#addFunction(IFunc)} 9 | */ 10 | 11 | public interface IFunc { 12 | 13 | /** 14 | * @return the icon of function. 15 | */ 16 | @DrawableRes 17 | int getIcon(); 18 | 19 | /** 20 | * @return the name of function. 21 | */ 22 | String getName(); 23 | 24 | /** 25 | * Click event. 26 | * 27 | * @return "Turn on" the state of the Func once return true, turn off otherwise. 28 | */ 29 | boolean onClick(); 30 | } 31 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/history/HistoryRecorder.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.history; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.os.HandlerThread; 8 | import android.os.Message; 9 | 10 | import tech.linjiang.pandora.cache.History; 11 | import tech.linjiang.pandora.ui.Dispatcher; 12 | 13 | /** 14 | * Created by linjiang on 2019/3/4. 15 | */ 16 | 17 | public class HistoryRecorder implements Application.ActivityLifecycleCallbacks { 18 | 19 | private static final int CODE = 0x02; 20 | private Handler handler; 21 | private Activity topActivity; 22 | 23 | public HistoryRecorder(Application application) { 24 | WorkThread thread = new WorkThread(); 25 | thread.start(); 26 | handler = new Handler(thread.getLooper(), thread); 27 | application.registerActivityLifecycleCallbacks(this); 28 | } 29 | 30 | @Override 31 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 32 | record(activity, "onCreate"); 33 | } 34 | 35 | @Override 36 | public void onActivityStarted(Activity activity) { 37 | record(activity, "onStart"); 38 | } 39 | 40 | @Override 41 | public void onActivityResumed(Activity activity) { 42 | record(activity, "onResume"); 43 | if (!(activity instanceof Dispatcher)) { 44 | topActivity = activity; 45 | } 46 | } 47 | 48 | @Override 49 | public void onActivityPaused(Activity activity) { 50 | record(activity, "onPause"); 51 | } 52 | 53 | @Override 54 | public void onActivityStopped(Activity activity) { 55 | record(activity, "onStop"); 56 | } 57 | 58 | @Override 59 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 60 | record(activity, "onSaveInstanceState"); 61 | } 62 | 63 | @Override 64 | public void onActivityDestroyed(Activity activity) { 65 | record(activity, "onDestroy"); 66 | if (topActivity == activity) { 67 | topActivity = null; 68 | } 69 | } 70 | 71 | private void record(Activity activity, String event) { 72 | History history = new History(); 73 | history.createTime = System.currentTimeMillis(); 74 | history.activity = activity.getClass().getSimpleName(); 75 | history.event = event; 76 | handler.sendMessage(Message.obtain(handler, CODE, history)); 77 | } 78 | 79 | public Activity getTopActivity() { 80 | return topActivity; 81 | } 82 | 83 | static class WorkThread extends HandlerThread implements Handler.Callback { 84 | 85 | WorkThread() { 86 | super("HistoryRecorder"); 87 | } 88 | 89 | @Override 90 | public boolean handleMessage(Message msg) { 91 | if (msg.what == CODE) { 92 | History.insert((History) msg.obj); 93 | } 94 | return true; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/CurInfoView.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.graphics.PixelFormat; 6 | import android.os.Build; 7 | import androidx.core.view.ViewCompat; 8 | import androidx.appcompat.widget.AppCompatTextView; 9 | import android.text.TextUtils; 10 | import android.view.Gravity; 11 | import android.view.WindowManager; 12 | import android.widget.FrameLayout; 13 | 14 | import tech.linjiang.pandora.util.Config; 15 | import tech.linjiang.pandora.util.Utils; 16 | import tech.linjiang.pandora.util.ViewKnife; 17 | 18 | /** 19 | * Created by linjiang on 2018/7/26. 20 | */ 21 | 22 | public class CurInfoView extends AppCompatTextView { 23 | public CurInfoView(Context context) { 24 | super(context); 25 | setBackgroundColor(0x6f000000); 26 | setTextSize(14); 27 | setTextColor(Color.WHITE); 28 | setGravity(Gravity.CENTER); 29 | setPadding(ViewKnife.dip2px(4), 0, ViewKnife.dip2px(4), 0); 30 | } 31 | 32 | 33 | private void open() { 34 | WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 35 | params.width = FrameLayout.LayoutParams.WRAP_CONTENT; 36 | params.height = FrameLayout.LayoutParams.WRAP_CONTENT; 37 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { 38 | params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; 39 | } else { 40 | params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 41 | } 42 | params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 43 | params.format = PixelFormat.TRANSLUCENT; 44 | params.gravity = Config.getUI_ACTIVITY_GRAVITY(); 45 | Utils.addViewToWindow(this, params); 46 | } 47 | 48 | private void close() { 49 | Utils.removeViewFromWindow(this); 50 | } 51 | 52 | public void toggle() { 53 | if (isOpen()) { 54 | close(); 55 | } else { 56 | open(); 57 | } 58 | } 59 | 60 | public boolean isOpen() { 61 | return ViewCompat.isAttachedToWindow(this); 62 | } 63 | 64 | private static CharSequence lastInfo; 65 | 66 | public void updateText(CharSequence value) { 67 | if (!TextUtils.isEmpty(value)) { 68 | lastInfo = getText(); 69 | } else { 70 | value = lastInfo; 71 | } 72 | setText(value); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/ElementHoldView.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import tech.linjiang.pandora.inspector.model.Element; 13 | import tech.linjiang.pandora.util.ViewKnife; 14 | 15 | /** 16 | * Created by linjiang on 09/06/2018. 17 | * 18 | * https://github.com/eleme/UETool/ 19 | */ 20 | 21 | public class ElementHoldView extends View { 22 | private static final String TAG = "ElementHoldView"; 23 | 24 | public ElementHoldView(Context context) { 25 | super(context); 26 | } 27 | 28 | private List elements = new ArrayList<>(); 29 | 30 | 31 | private void traverse(View view) { 32 | if (view.getAlpha() == 0 || view.getVisibility() != View.VISIBLE) return; 33 | elements.add(new Element(view)); 34 | if (view instanceof ViewGroup) { 35 | ViewGroup parent = (ViewGroup) view; 36 | for (int i = 0; i < parent.getChildCount(); i++) { 37 | traverse(parent.getChildAt(i)); 38 | } 39 | } 40 | } 41 | 42 | protected final Element getTargetElement(float x, float y) { 43 | Element target = null; 44 | for (int i = elements.size() - 1; i >= 0; i--) { 45 | final Element element = elements.get(i); 46 | if (element.getRect().contains((int) x, (int) y)) { 47 | if (isParentNotVisible(element.getParentElement())) { 48 | continue; 49 | } 50 | target = element; 51 | break; 52 | } 53 | } 54 | if (target == null) { 55 | Log.w(TAG, "getTargetElement: not find"); 56 | } 57 | return target; 58 | } 59 | 60 | protected final Element getTargetElement(View v) { 61 | Element target = null; 62 | for (int i = elements.size() - 1; i >= 0; i--) { 63 | final Element element = elements.get(i); 64 | if (element.getView() == v) { 65 | target = element; 66 | break; 67 | } 68 | } 69 | if (target == null) { 70 | Log.w(TAG, "getTargetElement: not find"); 71 | } 72 | return target; 73 | } 74 | 75 | 76 | protected final void resetAll() { 77 | for (Element e : elements) { 78 | if (e != null) { 79 | e.reset(); 80 | } 81 | } 82 | } 83 | 84 | private boolean isParentNotVisible(Element parent) { 85 | if (parent == null) { 86 | return false; 87 | } 88 | if (parent.getRect().left >= getMeasuredWidth() 89 | || parent.getRect().top >= getMeasuredHeight()) { 90 | return true; 91 | } else { 92 | return isParentNotVisible(parent.getParentElement()); 93 | } 94 | } 95 | 96 | @Override 97 | protected void onDetachedFromWindow() { 98 | super.onDetachedFromWindow(); 99 | elements.clear(); 100 | } 101 | 102 | public void tryGetFrontView(Activity targetActivity) { 103 | View decor = ViewKnife.tryGetTheFrontView(targetActivity); 104 | if (decor != null) { 105 | traverse(decor); 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/GridLineView.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Paint; 6 | import android.graphics.PixelFormat; 7 | import android.os.Build; 8 | import androidx.core.view.ViewCompat; 9 | import android.view.View; 10 | import android.view.WindowManager; 11 | import android.widget.FrameLayout; 12 | 13 | import tech.linjiang.pandora.util.Config; 14 | import tech.linjiang.pandora.util.Utils; 15 | import tech.linjiang.pandora.util.ViewKnife; 16 | 17 | /** 18 | * Created by linjiang on 2018/7/23. 19 | */ 20 | public class GridLineView extends View { 21 | 22 | public GridLineView(Context context) { 23 | super(context); 24 | } 25 | 26 | private void open() { 27 | LINE_INTERVAL = ViewKnife.dip2px(Config.getUI_GRID_INTERVAL()); 28 | WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 29 | params.width = FrameLayout.LayoutParams.MATCH_PARENT; 30 | params.height = FrameLayout.LayoutParams.MATCH_PARENT; 31 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { 32 | params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; 33 | } else { 34 | params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 35 | } 36 | params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 37 | params.format = PixelFormat.TRANSLUCENT; 38 | Utils.addViewToWindow(this, params); 39 | } 40 | 41 | private void close() { 42 | Utils.removeViewFromWindow(this); 43 | } 44 | 45 | public void toggle() { 46 | if (isOpen()) { 47 | close(); 48 | } else { 49 | open(); 50 | } 51 | } 52 | 53 | public boolean isOpen() { 54 | return ViewCompat.isAttachedToWindow(this); 55 | } 56 | 57 | private int LINE_INTERVAL; 58 | 59 | private Paint paint = new Paint() { 60 | { 61 | setAntiAlias(true); 62 | setColor(0x30000000); 63 | setStrokeWidth(1); 64 | } 65 | }; 66 | 67 | @Override 68 | protected void onDraw(Canvas canvas) { 69 | super.onDraw(canvas); 70 | int startX = 0; 71 | while (startX < getMeasuredWidth()) { 72 | canvas.drawLine(startX, 0, startX, getMeasuredHeight(), paint); 73 | startX = startX + LINE_INTERVAL; 74 | } 75 | 76 | int startY = 0; 77 | while (startY < getMeasuredHeight()) { 78 | canvas.drawLine(0, startY, getMeasuredWidth(), startY, paint); 79 | startY = startY + LINE_INTERVAL; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/attribute/AttrFactory.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector.attribute; 2 | 3 | import android.view.View; 4 | 5 | import java.lang.reflect.ParameterizedType; 6 | import java.lang.reflect.Type; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import tech.linjiang.pandora.inspector.attribute.parser.ImageViewParser; 11 | import tech.linjiang.pandora.inspector.attribute.parser.ViewGroupParser; 12 | import tech.linjiang.pandora.inspector.attribute.parser.TextViewParser; 13 | import tech.linjiang.pandora.inspector.attribute.parser.ViewParser; 14 | import tech.linjiang.pandora.inspector.model.Attribute; 15 | 16 | /** 17 | * Created by linjiang on 15/06/2018. 18 | */ 19 | 20 | public final class AttrFactory { 21 | 22 | 23 | private List parsers = new ArrayList() { 24 | { 25 | add(new ImageViewParser()); 26 | add(new TextViewParser()); 27 | add(new ViewGroupParser()); 28 | add(new ViewParser()); 29 | } 30 | }; 31 | 32 | public void addParser(IParser parser) { 33 | parsers.add(0, parser); 34 | } 35 | 36 | public List parse(View v) { 37 | List attributes = new ArrayList<>(); 38 | for (IParser parser : parsers) { 39 | if (parser != null) { 40 | try { 41 | ParameterizedType parameterizedType = 42 | (ParameterizedType) parser.getClass().getGenericInterfaces()[0]; 43 | Type actualTypeArguments = parameterizedType.getActualTypeArguments()[0]; 44 | if (findUpUntilEqual(v.getClass(), actualTypeArguments)) { 45 | List result = parser.getAttrs(v); 46 | if (result != null && !result.isEmpty()) { 47 | for (int i = 0; i < result.size(); i++) { 48 | result.get(i).category = actualTypeArguments.toString(); 49 | } 50 | attributes.addAll(result); 51 | } 52 | } 53 | } catch (Throwable t) { 54 | t.printStackTrace(); 55 | } 56 | } 57 | } 58 | return attributes; 59 | } 60 | private boolean findUpUntilEqual(Class clazz, Type type) { 61 | do { 62 | if (type == clazz) { 63 | return true; 64 | } 65 | clazz = clazz.getSuperclass(); 66 | } while (clazz != null && clazz != Object.class); 67 | return false; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/attribute/IParser.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector.attribute; 2 | 3 | import android.view.View; 4 | 5 | import java.util.List; 6 | 7 | import tech.linjiang.pandora.inspector.model.Attribute; 8 | 9 | /** 10 | * Created by linjiang on 2018/6/19. 11 | *

12 | * T is the supported View type and its subclasses are also parsed 13 | */ 14 | 15 | public interface IParser { 16 | List getAttrs(T view); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/attribute/parser/ImageViewParser.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector.attribute.parser; 2 | 3 | import android.widget.ImageView; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import tech.linjiang.pandora.inspector.attribute.IParser; 9 | import tech.linjiang.pandora.inspector.model.Attribute; 10 | 11 | /** 12 | * Created by linjiang on 2018/6/19. 13 | */ 14 | 15 | public class ImageViewParser implements IParser { 16 | 17 | @Override 18 | public List getAttrs(ImageView view) { 19 | List attributes = new ArrayList<>(); 20 | Attribute scaleTypeAttribute = new Attribute("scaleType", scaleTypeToStr(view.getScaleType()), Attribute.Edit.SCALE_TYPE); 21 | attributes.add(scaleTypeAttribute); 22 | return attributes; 23 | } 24 | private static String scaleTypeToStr(ImageView.ScaleType scaleType) { 25 | switch (scaleType) { 26 | case CENTER: 27 | return "CENTER"; 28 | case FIT_XY: 29 | return "FIT_XY"; 30 | case MATRIX: 31 | return "MATRIX"; 32 | case FIT_END: 33 | return "FIT_END"; 34 | case FIT_START: 35 | return "FIT_START"; 36 | case FIT_CENTER: 37 | return "FIT_CENTER"; 38 | case CENTER_CROP: 39 | return "CENTER_CROP"; 40 | case CENTER_INSIDE: 41 | return "CENTER_INSIDE"; 42 | default: 43 | return "OTHER"; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/attribute/parser/ViewGroupParser.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector.attribute.parser; 2 | 3 | import android.view.ViewGroup; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import tech.linjiang.pandora.inspector.attribute.IParser; 9 | import tech.linjiang.pandora.inspector.model.Attribute; 10 | 11 | /** 12 | * Created by linjiang on 2018/6/19. 13 | */ 14 | 15 | public class ViewGroupParser implements IParser { 16 | 17 | @Override 18 | public List getAttrs(ViewGroup view) { 19 | List attributes = new ArrayList<>(); 20 | 21 | Attribute childCountDrawAttribute = new Attribute("childCount", String.valueOf(view.getChildCount())); 22 | attributes.add(childCountDrawAttribute); 23 | 24 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { 25 | Attribute clipChildrenDrawAttribute = new Attribute("clipChildren", String.valueOf(view.getClipChildren())); 26 | attributes.add(clipChildrenDrawAttribute); 27 | } 28 | 29 | Attribute willNotDrawAttribute = new Attribute("willNotDraw", String.valueOf(view.willNotDraw())); 30 | attributes.add(willNotDrawAttribute); 31 | 32 | return attributes; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/canvas/GridCanvas.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector.canvas; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import android.view.View; 6 | 7 | import tech.linjiang.pandora.util.ViewKnife; 8 | 9 | /** 10 | * Created by linjiang on 12/06/2018. 11 | */ 12 | 13 | public class GridCanvas { 14 | 15 | private static final int LINE_INTERVAL = ViewKnife.dip2px(5); 16 | 17 | private View container; 18 | 19 | public GridCanvas(View container) { 20 | this.container = container; 21 | } 22 | 23 | private Paint paint = new Paint() { 24 | { 25 | setAntiAlias(true); 26 | setColor(0xff555555); 27 | setStrokeWidth(1); 28 | } 29 | }; 30 | 31 | private int getMeasuredWidth() { 32 | return container.getMeasuredWidth(); 33 | } 34 | 35 | private int getMeasuredHeight() { 36 | return container.getMeasuredHeight(); 37 | } 38 | 39 | public void draw(Canvas canvas, float alpha) { 40 | canvas.save(); 41 | int startX = 0; 42 | paint.setAlpha((int) (255 * alpha)); 43 | while (startX < getMeasuredWidth()) { 44 | canvas.drawLine(startX, 0, startX, getMeasuredHeight(), paint); 45 | startX = startX + LINE_INTERVAL; 46 | } 47 | 48 | int startY = 0; 49 | while (startY < getMeasuredHeight()) { 50 | canvas.drawLine(0, startY, getMeasuredWidth(), startY, paint); 51 | startY = startY + LINE_INTERVAL; 52 | } 53 | canvas.restore(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/canvas/SelectCanvas.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector.canvas; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Color; 5 | import android.graphics.DashPathEffect; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.view.View; 9 | 10 | import tech.linjiang.pandora.inspector.model.Element; 11 | import tech.linjiang.pandora.util.ViewKnife; 12 | 13 | /** 14 | * Created by linjiang on 11/06/2018. 15 | */ 16 | 17 | public class SelectCanvas { 18 | 19 | private View container; 20 | private final int cornerRadius = ViewKnife.dip2px(1.5f); 21 | private Paint cornerPaint = new Paint() { 22 | { 23 | setAntiAlias(true); 24 | setStrokeWidth(ViewKnife.dip2px(1)); 25 | } 26 | }; 27 | private Paint areaPaint = new Paint() { 28 | { 29 | setAntiAlias(true); 30 | setColor(Color.RED); 31 | setStyle(Style.STROKE); 32 | setStrokeWidth(ViewKnife.dip2px(1)); 33 | } 34 | }; 35 | private Paint dashLinePaint = new Paint() { 36 | { 37 | setAntiAlias(true); 38 | setColor(0xaaFF0000); 39 | setStyle(Style.STROKE); 40 | setPathEffect(new DashPathEffect(new float[]{ViewKnife.dip2px(3), ViewKnife.dip2px(3)}, 0)); 41 | } 42 | }; 43 | 44 | public SelectCanvas(View container) { 45 | container.setLayerType(View.LAYER_TYPE_SOFTWARE, null); 46 | this.container = container; 47 | } 48 | 49 | private int getMeasuredWidth() { 50 | return container.getMeasuredWidth(); 51 | } 52 | 53 | private int getMeasuredHeight() { 54 | return container.getMeasuredHeight(); 55 | } 56 | 57 | 58 | public void draw(Canvas canvas, Element... elements) { 59 | canvas.save(); 60 | for (Element element : elements) { 61 | if (element != null) { 62 | drawSelected(canvas, element); 63 | } 64 | } 65 | canvas.restore(); 66 | } 67 | 68 | private void drawSelected(Canvas canvas, Element element) { 69 | Rect rect = element.getRect(); 70 | canvas.drawLine(0, rect.top, getMeasuredWidth(), rect.top, dashLinePaint); 71 | canvas.drawLine(0, rect.bottom, getMeasuredWidth(), rect.bottom, dashLinePaint); 72 | canvas.drawLine(rect.left, 0, rect.left, getMeasuredHeight(), dashLinePaint); 73 | canvas.drawLine(rect.right, 0, rect.right, getMeasuredHeight(), dashLinePaint); 74 | canvas.drawRect(rect, areaPaint); 75 | cornerPaint.setColor(Color.WHITE); 76 | cornerPaint.setStyle(Paint.Style.FILL); 77 | canvas.drawCircle(rect.left, rect.top, cornerRadius, cornerPaint); 78 | canvas.drawCircle(rect.right, rect.top, cornerRadius, cornerPaint); 79 | canvas.drawCircle(rect.left, rect.bottom, cornerRadius, cornerPaint); 80 | canvas.drawCircle(rect.right, rect.bottom, cornerRadius, cornerPaint); 81 | cornerPaint.setColor(Color.RED); 82 | cornerPaint.setStyle(Paint.Style.STROKE); 83 | canvas.drawCircle(rect.left, rect.top, cornerRadius, cornerPaint); 84 | canvas.drawCircle(rect.right, rect.top, cornerRadius, cornerPaint); 85 | canvas.drawCircle(rect.left, rect.bottom, cornerRadius, cornerPaint); 86 | canvas.drawCircle(rect.right, rect.bottom, cornerRadius, cornerPaint); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/model/Attribute.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector.model; 2 | 3 | import androidx.annotation.IntDef; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | /** 9 | * Created by linjiang on 15/06/2018. 10 | */ 11 | 12 | public class Attribute { 13 | 14 | // For classification 15 | public String category; 16 | public String attrName; 17 | public String attrValue; 18 | 19 | 20 | public Attribute(String attrName, String attrValue) { 21 | this.attrName = attrName; 22 | this.attrValue = attrValue; 23 | } 24 | 25 | public Attribute(String attrName, String attrValue, @Edit int attrType) { 26 | this.attrName = attrName; 27 | this.attrValue = attrValue; 28 | this.attrType = attrType; 29 | } 30 | 31 | // Some attributes that can be edited 32 | public @Edit int attrType = Edit.NORMAL; 33 | 34 | @IntDef({ 35 | Edit.NORMAL, 36 | Edit.LAYOUT_WIDTH, 37 | Edit.LAYOUT_HEIGHT, 38 | Edit.VISIBILITY, 39 | Edit.PADDING_LEFT, 40 | Edit.PADDING_RIGHT, 41 | Edit.PADDING_TOP, 42 | Edit.PADDING_BOTTOM, 43 | Edit.ALPHA, 44 | Edit.TEXT, 45 | Edit.TEXT_COLOR, 46 | Edit.TEXT_SIZE, 47 | Edit.SCALE_TYPE, 48 | Edit.MARGIN_LEFT, 49 | Edit.MARGIN_RIGHT, 50 | Edit.MARGIN_TOP, 51 | Edit.MARGIN_BOTTOM, 52 | }) 53 | @Retention(RetentionPolicy.SOURCE) 54 | public @interface Edit { 55 | int NORMAL = 0x00; 56 | int LAYOUT_WIDTH = 0x01; 57 | int LAYOUT_HEIGHT = 0x02; 58 | int VISIBILITY = 0x03; 59 | int PADDING_LEFT = 0x04; 60 | int PADDING_RIGHT = 0x05; 61 | int PADDING_TOP = 0x06; 62 | int PADDING_BOTTOM = 0x07; 63 | int ALPHA = 0x08; 64 | 65 | int TEXT_SIZE = 0x10; 66 | int TEXT_COLOR = 0x11; 67 | int TEXT = 0x12; 68 | int SCALE_TYPE = 0x13; 69 | 70 | int MARGIN_LEFT = 0x14; 71 | int MARGIN_RIGHT = 0x15; 72 | int MARGIN_TOP = 0x16; 73 | int MARGIN_BOTTOM = 0x17; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/inspector/model/Element.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.inspector.model; 2 | 3 | import android.graphics.Rect; 4 | import android.os.Build; 5 | import android.view.View; 6 | 7 | import tech.linjiang.pandora.util.ViewKnife; 8 | 9 | public class Element { 10 | 11 | private View view; 12 | private Rect originRect = new Rect(); 13 | private Rect rect = new Rect(); 14 | private int[] location = new int[2]; 15 | private Element parentElement; 16 | 17 | public Element(View view) { 18 | this.view = view; 19 | reset(); 20 | originRect.set(rect.left, rect.top, rect.right, rect.bottom); 21 | } 22 | 23 | public View getView() { 24 | return view; 25 | } 26 | 27 | public Rect getRect() { 28 | return rect; 29 | } 30 | 31 | public Rect getOriginRect() { 32 | return originRect; 33 | } 34 | 35 | public void reset() { 36 | view.getLocationOnScreen(location); 37 | int width = view.getWidth(); 38 | int height = view.getHeight(); 39 | 40 | int left = location[0]; 41 | int right = left + width; 42 | int top = location[1]; 43 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 44 | top -= ViewKnife.getStatusHeight(); 45 | } 46 | int bottom = top + height; 47 | 48 | rect.set(left, top, right, bottom); 49 | } 50 | 51 | public Element getParentElement() { 52 | if (parentElement == null) { 53 | Object parentView = view.getParent(); 54 | if (parentView instanceof View) { 55 | parentElement = new Element((View) parentView); 56 | } 57 | } 58 | return parentElement; 59 | } 60 | 61 | public void offset(float dx, float dy) { 62 | view.setTranslationX(view.getTranslationX() + dx); 63 | view.setTranslationY(view.getTranslationY() + dy); 64 | } 65 | 66 | @Override 67 | public boolean equals(Object o) { 68 | if (this == o) return true; 69 | if (o == null || getClass() != o.getClass()) return false; 70 | 71 | Element element = (Element) o; 72 | 73 | return view != null ? view.equals(element.view) : element.view == null; 74 | 75 | } 76 | 77 | @Override 78 | public int hashCode() { 79 | return view != null ? view.hashCode() : 0; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/network/NetStateListener.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.network; 2 | 3 | /** 4 | * Created by linjiang on 2018/6/21. 5 | *

6 | * event emits on UI thread after data was inserted. 7 | */ 8 | 9 | public interface NetStateListener { 10 | 11 | void onRequestStart(long id); 12 | 13 | void onRequestEnd(long id); 14 | } 15 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/preference/SharedPref.java: -------------------------------------------------------------------------------- 1 | 2 | package tech.linjiang.pandora.preference; 3 | 4 | import java.io.File; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import tech.linjiang.pandora.preference.protocol.IProvider; 10 | import tech.linjiang.pandora.util.Utils; 11 | 12 | public final class SharedPref { 13 | 14 | private List providers = new ArrayList<>(); 15 | private SharedPrefDriver driver; 16 | 17 | public SharedPref() { 18 | driver = new SharedPrefDriver(Utils.getContext()); 19 | providers.add(new SharedPrefProvider()); 20 | } 21 | 22 | public SharedPref addProvider(IProvider provider) { 23 | providers.add(provider); 24 | return this; 25 | } 26 | 27 | public List getSharedPrefDescs() { 28 | List descriptors = new ArrayList<>(); 29 | for (int i = 0; i < providers.size(); i++) { 30 | descriptors.addAll(driver.getSharedPrefDescs(providers.get(i))); 31 | } 32 | return descriptors; 33 | } 34 | 35 | public Map getSharedPrefContent(File descriptor) { 36 | return driver.getSharedPrefContent(descriptor); 37 | } 38 | 39 | public String updateSharedPref(File descriptor, String key, String value) { 40 | try { 41 | driver.updateSharedPref(descriptor, key, value); 42 | return null; 43 | } catch (Throwable t) { 44 | t.printStackTrace(); 45 | return t.getMessage(); 46 | } 47 | } 48 | 49 | public String removeSharedPrefKey(File descriptor, String key) { 50 | try { 51 | driver.removeSharedPrefKey(descriptor, key); 52 | return null; 53 | } catch (Throwable t) { 54 | t.printStackTrace(); 55 | return t.getMessage(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/preference/SharedPrefProvider.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.preference; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | 6 | import java.io.File; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import tech.linjiang.pandora.preference.protocol.IProvider; 11 | 12 | /** 13 | * Created by linjiang on 04/06/2018. 14 | */ 15 | 16 | public class SharedPrefProvider implements IProvider { 17 | 18 | @Override 19 | public List getSharedPrefFiles(Context context) { 20 | List files = new ArrayList<>(); 21 | buildWithPath(context.getApplicationInfo().dataDir, files); 22 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 23 | buildWithPath(context.getApplicationInfo().deviceProtectedDataDir, files); 24 | } 25 | return files; 26 | } 27 | 28 | private void buildWithPath(String path, List container) { 29 | File root = new File(path + "/shared_prefs"); 30 | if (root.exists()) { 31 | for (File file : root.listFiles()) { 32 | if (file.getName().endsWith(DEF_SUFFIX)) { 33 | container.add(file); 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/preference/protocol/IProvider.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.preference.protocol; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.File; 6 | import java.util.List; 7 | 8 | /** 9 | * Created by linjiang on 04/06/2018. 10 | */ 11 | 12 | public interface IProvider { 13 | String DEF_SUFFIX = ".xml"; 14 | 15 | List getSharedPrefFiles(Context context); 16 | } 17 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/sandbox/Sandbox.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.sandbox; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | 6 | import java.io.File; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | import tech.linjiang.pandora.util.Utils; 12 | 13 | /** 14 | * Created by linjiang on 04/06/2018. 15 | */ 16 | 17 | public class Sandbox { 18 | 19 | private static String ROOT_PATH = Utils.getContext().getApplicationInfo().dataDir; 20 | 21 | public static List getRootFiles() { 22 | return getFiles(new File(ROOT_PATH)); 23 | } 24 | 25 | @TargetApi(Build.VERSION_CODES.N) 26 | public static List getDPMFiles() { 27 | return getFiles(new File(Utils.getContext().getApplicationInfo().deviceProtectedDataDir)); 28 | } 29 | 30 | public static List getFiles(File curFile) { 31 | List descriptors = new ArrayList<>(); 32 | if (curFile.isDirectory() && curFile.exists()) { 33 | descriptors.addAll(Arrays.asList(curFile.listFiles())); 34 | } 35 | return descriptors; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/TransActivity.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui; 2 | 3 | /** 4 | * Created by linjiang on 2018/9/15. 5 | */ 6 | 7 | public class TransActivity extends Dispatcher { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/connector/SimpleOnActionExpandListener.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.connector; 2 | 3 | import androidx.core.view.MenuItemCompat; 4 | import android.view.MenuItem; 5 | 6 | /** 7 | * Created by linjiang on 07/06/2018. 8 | */ 9 | 10 | public class SimpleOnActionExpandListener 11 | implements MenuItem.OnActionExpandListener, MenuItemCompat.OnActionExpandListener { 12 | @Override 13 | public boolean onMenuItemActionExpand(MenuItem item) { 14 | // true means menuView can expand 15 | return true; 16 | } 17 | 18 | @Override 19 | public boolean onMenuItemActionCollapse(MenuItem item) { 20 | // true means menuView can collapse 21 | return true; 22 | } 23 | 24 | public static void bind(MenuItem menuItem, final SimpleOnActionExpandListener callback) { 25 | try { 26 | menuItem.setOnActionExpandListener(new SimpleOnActionExpandListener() { 27 | @Override 28 | public boolean onMenuItemActionCollapse(MenuItem item) { 29 | return callback.onMenuItemActionCollapse(item); 30 | } 31 | 32 | @Override 33 | public boolean onMenuItemActionExpand(MenuItem item) { 34 | return callback.onMenuItemActionExpand(item); 35 | } 36 | }); 37 | } catch (UnsupportedOperationException e) { 38 | MenuItemCompat.setOnActionExpandListener(menuItem, new SimpleOnActionExpandListener() { 39 | @Override 40 | public boolean onMenuItemActionExpand(MenuItem item) { 41 | return callback.onMenuItemActionCollapse(item); 42 | } 43 | 44 | @Override 45 | public boolean onMenuItemActionCollapse(MenuItem item) { 46 | return callback.onMenuItemActionExpand(item); 47 | } 48 | }); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/connector/SimpleOnQueryTextListener.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.connector; 2 | 3 | import androidx.appcompat.widget.SearchView; 4 | 5 | /** 6 | * Created by linjiang on 07/06/2018. 7 | */ 8 | 9 | public class SimpleOnQueryTextListener implements SearchView.OnQueryTextListener { 10 | @Override 11 | public boolean onQueryTextSubmit(String query) { 12 | return false; 13 | } 14 | 15 | @Override 16 | public boolean onQueryTextChange(String newText) { 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/connector/Type.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.connector; 2 | 3 | import androidx.annotation.IntDef; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | @IntDef({ 9 | Type.NET, 10 | Type.FILE, 11 | Type.HIERARCHY, 12 | Type.BASELINE, 13 | Type.SELECT, 14 | Type.CONFIG, 15 | Type.BUG, 16 | Type.HISTORY, 17 | Type.ROUTE, 18 | Type.PERMISSION 19 | }) 20 | @Retention(RetentionPolicy.SOURCE) 21 | public @interface Type { 22 | int NET = 0x01; 23 | int FILE = 0x02; 24 | int HIERARCHY = 0x03; 25 | int BASELINE = 0x05; 26 | int SELECT = 0x06; 27 | int CONFIG = 0x07; 28 | int BUG = 0x08; 29 | int HISTORY = 0x09; 30 | int ROUTE = 0x10; 31 | int PERMISSION = 0x11; 32 | } -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/connector/UIStateCallback.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.connector; 2 | 3 | /** 4 | * Created by linjiang on 04/06/2018. 5 | */ 6 | 7 | public interface UIStateCallback { 8 | void showHint(); 9 | void hideHint(); 10 | } 11 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/fragment/BaseListFragment.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.fragment; 2 | 3 | import android.graphics.drawable.GradientDrawable; 4 | import androidx.recyclerview.widget.DividerItemDecoration; 5 | import androidx.recyclerview.widget.LinearLayoutManager; 6 | import androidx.recyclerview.widget.RecyclerView; 7 | import android.view.View; 8 | 9 | import tech.linjiang.pandora.core.R; 10 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 11 | import tech.linjiang.pandora.ui.view.MenuRecyclerView; 12 | import tech.linjiang.pandora.util.ViewKnife; 13 | 14 | /** 15 | * Created by linjiang on 2018/6/22. 16 | */ 17 | 18 | public class BaseListFragment extends BaseFragment { 19 | @Override 20 | protected final int getLayoutId() { 21 | return 0; 22 | } 23 | 24 | @Override 25 | protected View getLayoutView() { 26 | adapter = new UniversalAdapter(); 27 | recyclerView = new MenuRecyclerView(getContext()); 28 | recyclerView.setBackgroundColor(ViewKnife.getColor(R.color.pd_main_bg)); 29 | recyclerView.setLayoutManager(onCreateLayoutManager()); 30 | if (needDefaultDivider()) { 31 | DividerItemDecoration divider = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL); 32 | GradientDrawable horizontalDrawable = new GradientDrawable(); 33 | horizontalDrawable.setColor(0xffE5E5E5); 34 | horizontalDrawable.setSize(0, 1); 35 | divider.setDrawable(horizontalDrawable); 36 | recyclerView.addItemDecoration(divider); 37 | } 38 | recyclerView.setAdapter(adapter); 39 | return recyclerView; 40 | } 41 | 42 | protected boolean needDefaultDivider() { 43 | return true; 44 | } 45 | 46 | private MenuRecyclerView recyclerView; 47 | private UniversalAdapter adapter; 48 | 49 | protected final MenuRecyclerView getRecyclerView() { 50 | return recyclerView; 51 | } 52 | 53 | public final UniversalAdapter getAdapter() { 54 | return adapter; 55 | } 56 | 57 | protected RecyclerView.LayoutManager onCreateLayoutManager() { 58 | return new LinearLayoutManager(getContext()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/fragment/DBFragment.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.fragment; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.Nullable; 5 | import android.view.View; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.Locale; 11 | 12 | import tech.linjiang.pandora.Pandora; 13 | import tech.linjiang.pandora.ui.item.NameItem; 14 | import tech.linjiang.pandora.ui.item.TitleItem; 15 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 16 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 17 | 18 | /** 19 | * Created by linjiang on 03/06/2018. 20 | */ 21 | 22 | public class DBFragment extends BaseListFragment { 23 | 24 | @Override 25 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 26 | super.onViewCreated(view, savedInstanceState); 27 | final int key = getArguments().getInt(PARAM1); 28 | 29 | List tables = Pandora.get().getDatabases().getTableNames(key); 30 | Collections.sort(tables); 31 | List data = new ArrayList<>(tables.size()); 32 | data.add(new TitleItem(String.format(Locale.getDefault(), "%d TABLES", tables.size()))); 33 | for (int i = 0; i < tables.size(); i++) { 34 | data.add(new NameItem(tables.get(i))); 35 | } 36 | getAdapter().setItems(data); 37 | 38 | getAdapter().setListener(new UniversalAdapter.OnItemClickListener() { 39 | @Override 40 | public void onItemClick(int position, BaseItem item) { 41 | if (item instanceof NameItem) { 42 | Bundle bundle = new Bundle(); 43 | bundle.putInt(PARAM1, key); 44 | bundle.putString(PARAM2, ((NameItem) item).data); 45 | launch(TableFragment.class, ((NameItem) item).data, bundle); 46 | } 47 | } 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/fragment/HistoryFragment.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.fragment; 2 | 3 | 4 | import android.os.Bundle; 5 | import androidx.annotation.Nullable; 6 | import androidx.appcompat.widget.Toolbar; 7 | import android.view.MenuItem; 8 | import android.view.View; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import tech.linjiang.pandora.cache.History; 14 | import tech.linjiang.pandora.core.R; 15 | import tech.linjiang.pandora.ui.item.KeyValueItem; 16 | import tech.linjiang.pandora.ui.item.TitleItem; 17 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 18 | import tech.linjiang.pandora.util.SimpleTask; 19 | import tech.linjiang.pandora.util.Utils; 20 | 21 | /** 22 | * Created by linjiang on 2019/3/4. 23 | */ 24 | 25 | public class HistoryFragment extends BaseListFragment { 26 | 27 | @Override 28 | protected boolean enableSwipeBack() { 29 | return false; 30 | } 31 | 32 | @Override 33 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 34 | super.onViewCreated(view, savedInstanceState); 35 | getToolbar().setTitle("Activity History"); 36 | getToolbar().getMenu().add(-1, 0, 0, R.string.pd_name_delete_key).setIcon(R.drawable.pd_delete) 37 | .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); 38 | getToolbar().setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { 39 | @Override 40 | public boolean onMenuItemClick(MenuItem item) { 41 | History.clear(); 42 | getAdapter().clearItems(); 43 | Utils.toast(R.string.pd_success); 44 | return true; 45 | } 46 | }); 47 | loadData(); 48 | } 49 | 50 | private void loadData() { 51 | hideError(); 52 | showLoading(); 53 | new SimpleTask<>(new SimpleTask.Callback>() { 54 | @Override 55 | public List doInBackground(Void[] params) { 56 | return History.query(); 57 | } 58 | 59 | @Override 60 | public void onPostExecute(List result) { 61 | hideLoading(); 62 | List data = new ArrayList<>(result.size()); 63 | if (Utils.isNotEmpty(result)) { 64 | data.add(new TitleItem("Task")); 65 | for (History history : result) { 66 | String[] value = new String[2]; 67 | value[0] = history.activity; 68 | value[1] = history.event; 69 | data.add(new KeyValueItem(value, false, false, Utils.millis2String(history.createTime, Utils.HHMMSS))); 70 | } 71 | getAdapter().setItems(data); 72 | } else { 73 | showError(null); 74 | } 75 | } 76 | }).execute(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/fragment/MeasureFragment.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.fragment; 2 | 3 | 4 | import android.os.Bundle; 5 | import androidx.annotation.Nullable; 6 | import androidx.appcompat.widget.Toolbar; 7 | import android.view.View; 8 | 9 | import tech.linjiang.pandora.core.R; 10 | import tech.linjiang.pandora.inspector.BaseLineView; 11 | import tech.linjiang.pandora.ui.GeneralDialog; 12 | 13 | /** 14 | * Created by linjiang on 2019/3/5. 15 | */ 16 | 17 | public class MeasureFragment extends BaseFragment { 18 | 19 | @Override 20 | protected Toolbar onCreateToolbar() { 21 | return null; 22 | } 23 | 24 | @Override 25 | protected boolean enableSwipeBack() { 26 | return false; 27 | } 28 | 29 | @Override 30 | protected int getLayoutId() { 31 | return 0; 32 | } 33 | 34 | @Override 35 | protected View getLayoutView() { 36 | return new BaseLineView(getContext()); 37 | } 38 | @Override 39 | public void onCreate(@Nullable Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | if (savedInstanceState != null) { 42 | return; 43 | } 44 | GeneralDialog.build(-1) 45 | .title(R.string.pd_help_title) 46 | .message(R.string.pd_help_baseline) 47 | .positiveButton(R.string.pd_ok) 48 | .show(this); 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/fragment/PermissionReqFragment.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.fragment; 2 | 3 | 4 | import android.app.Activity; 5 | import android.content.ActivityNotFoundException; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | import android.os.Build; 9 | import android.os.Bundle; 10 | import android.provider.Settings; 11 | import androidx.annotation.Nullable; 12 | import androidx.fragment.app.Fragment; 13 | 14 | import tech.linjiang.pandora.core.R; 15 | import tech.linjiang.pandora.ui.GeneralDialog; 16 | 17 | /** 18 | * Created by linjiang on 2019/3/5. 19 | */ 20 | 21 | public class PermissionReqFragment extends Fragment { 22 | 23 | private final int code = 0x10; 24 | @Override 25 | public void onCreate(@Nullable Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | if (savedInstanceState != null) { 28 | return; 29 | } 30 | GeneralDialog.build(code) 31 | .title(R.string.pd_permission_title) 32 | .message(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M 33 | ? R.string.pd_please_allow_permission : R.string.pd_permission_not_sure) 34 | .positiveButton(R.string.pd_ok) 35 | .negativeButton(R.string.pd_cancel) 36 | .cancelable(false) 37 | .show(this); 38 | } 39 | 40 | @Override 41 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 42 | super.onActivityResult(requestCode, resultCode, data); 43 | if (code == requestCode) { 44 | if (resultCode == Activity.RESULT_OK) { 45 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 46 | if (!Settings.canDrawOverlays(getContext())) { 47 | try { 48 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); 49 | intent.setData(Uri.parse("package:" + getContext().getPackageName())); 50 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 51 | getActivity().startActivity(intent); 52 | } catch (ActivityNotFoundException e) { 53 | e.printStackTrace(); 54 | } 55 | } 56 | } 57 | } 58 | getActivity().finish(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/fragment/RouteFragment.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.fragment; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import androidx.annotation.Nullable; 7 | import android.view.View; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import tech.linjiang.pandora.core.R; 13 | import tech.linjiang.pandora.ui.item.RouteItem; 14 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 15 | import tech.linjiang.pandora.util.Utils; 16 | 17 | /** 18 | * Created by linjiang on 2019/1/13. 19 | */ 20 | 21 | public class RouteFragment extends BaseListFragment { 22 | 23 | @Override 24 | protected boolean enableSwipeBack() { 25 | return false; 26 | } 27 | 28 | @Override 29 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 30 | super.onViewCreated(view, savedInstanceState); 31 | getToolbar().setTitle(R.string.pd_name_navigate); 32 | List activities = Utils.getActivities(); 33 | List data = new ArrayList<>(); 34 | for (int i = 0; i < activities.size(); i++) { 35 | data.add(new RouteItem(activities.get(i), callback)); 36 | } 37 | getAdapter().setItems(data); 38 | } 39 | 40 | @Override 41 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 42 | super.onActivityResult(requestCode, resultCode, data); 43 | if (requestCode == CODE1 && resultCode == Activity.RESULT_OK) { 44 | go(data); 45 | } 46 | } 47 | 48 | private final RouteItem.Callback callback = new RouteItem.Callback() { 49 | @Override 50 | public void onClick(String simpleName, String clazz, boolean needParam) { 51 | if (needParam) { 52 | Bundle bundle = new Bundle(); 53 | bundle.putString(PARAM1, simpleName); 54 | bundle.putString(PARAM2, clazz); 55 | launch(RouteParamFragment.class, bundle, CODE1); 56 | return; 57 | } 58 | try { 59 | Intent intent = new Intent(getContext(), Class.forName(clazz)); 60 | go(intent); 61 | } catch (ClassNotFoundException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | }; 66 | 67 | private void go(Intent intent) { 68 | startActivity(intent); 69 | getActivity().finish(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/CheckBoxItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import androidx.appcompat.widget.AppCompatCheckBox; 4 | 5 | import tech.linjiang.pandora.core.R; 6 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 7 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 8 | 9 | /** 10 | * Created by linjiang on 2018/7/24. 11 | */ 12 | 13 | public class CheckBoxItem extends BaseItem { 14 | 15 | private String title; 16 | 17 | public CheckBoxItem(String title, Boolean data) { 18 | super(data); 19 | this.title = title; 20 | } 21 | 22 | @Override 23 | public void onBinding(int position, UniversalAdapter.ViewPool pool, Boolean data) { 24 | ((AppCompatCheckBox)pool.itemView).setChecked(data); 25 | ((AppCompatCheckBox)pool.itemView).setText(title); 26 | } 27 | 28 | @Override 29 | public int getLayout() { 30 | return R.layout.pd_item_checkbox; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/ContentItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.graphics.Color; 4 | import android.view.ViewGroup; 5 | import android.widget.TextView; 6 | 7 | import tech.linjiang.pandora.core.R; 8 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 9 | import tech.linjiang.pandora.util.ViewKnife; 10 | 11 | /** 12 | * Created by linjiang on 06/06/2018. 13 | */ 14 | 15 | public class ContentItem extends NameItem { 16 | 17 | private boolean focus; 18 | 19 | public void setFocus(boolean focus) { 20 | this.focus = focus; 21 | } 22 | 23 | public ContentItem(String data) { 24 | super(data); 25 | } 26 | 27 | @Override 28 | public void onBinding(int position, UniversalAdapter.ViewPool pool, String data) { 29 | pool.getView(R.id.db_list_item_wrapper).getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; 30 | ((TextView) pool.getView(R.id.common_item_title)).setSingleLine(false); 31 | pool.setBackgroundColor(R.id.db_list_item_wrapper, 32 | focus ? ViewKnife.getColor(R.color.pd_item_focus) : Color.TRANSPARENT); 33 | super.onBinding(position, pool, data); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/CrashItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.text.TextUtils; 4 | import android.view.View; 5 | 6 | import tech.linjiang.pandora.cache.Crash; 7 | import tech.linjiang.pandora.core.R; 8 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 9 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 10 | import tech.linjiang.pandora.util.Utils; 11 | 12 | /** 13 | * Created by linjiang on 04/06/2018. 14 | */ 15 | 16 | public class CrashItem extends BaseItem { 17 | 18 | public CrashItem(Crash data) { 19 | super(data); 20 | } 21 | 22 | @Override 23 | public void onBinding(int position, UniversalAdapter.ViewPool pool, Crash data) { 24 | pool 25 | .setVisibility(R.id.common_item_arrow, View.VISIBLE) 26 | .setText(R.id.common_item_info, TextUtils.isEmpty(data.cause) ? data.type : data.cause) 27 | .setText(R.id.common_item_title, Utils.millis2String(data.createTime, Utils.HHMMSS)); 28 | } 29 | 30 | @Override 31 | public int getLayout() { 32 | return R.layout.pd_item_common; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/DBItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | /** 4 | * Created by linjiang on 05/06/2018. 5 | */ 6 | 7 | public class DBItem extends NameItem { 8 | 9 | public int key; 10 | 11 | public DBItem(String data, int key) { 12 | super(data); 13 | this.key = key; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/ExceptionItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import tech.linjiang.pandora.core.R; 4 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 5 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 6 | 7 | public class ExceptionItem extends BaseItem { 8 | public ExceptionItem(String data) { 9 | super(data); 10 | } 11 | 12 | @Override 13 | public void onBinding(int position, UniversalAdapter.ViewPool pool, String data) { 14 | pool.setText(R.id.text, data); 15 | } 16 | 17 | @Override 18 | public int getLayout() { 19 | return R.layout.pd_item_exception; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/FileItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.view.View; 4 | 5 | import java.io.File; 6 | import java.util.Locale; 7 | 8 | import tech.linjiang.pandora.core.R; 9 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 10 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 11 | import tech.linjiang.pandora.util.FileUtil; 12 | import tech.linjiang.pandora.util.Utils; 13 | 14 | /** 15 | * Created by linjiang on 04/06/2018. 16 | */ 17 | 18 | public class FileItem extends BaseItem { 19 | private String info; 20 | 21 | public FileItem(File data) { 22 | super(data); 23 | if (!data.isDirectory()) { 24 | info = String.format(Locale.getDefault(), "%s %s", 25 | FileUtil.fileSize(data), Utils.millis2String(data.lastModified(), Utils.NO_MILLIS)); 26 | } else { 27 | info = String.format(Locale.getDefault(), "%d items %s", 28 | Utils.getCount(data.list()), Utils.millis2String(data.lastModified(), Utils.NO_MILLIS)); 29 | } 30 | } 31 | 32 | @Override 33 | public void onBinding(int position, UniversalAdapter.ViewPool pool, File data) { 34 | pool 35 | .setVisibility(R.id.common_item_arrow, data.isDirectory() ? View.VISIBLE : View.GONE) 36 | .setText(R.id.common_item_title, data.getName()) 37 | .setText(R.id.common_item_info, info); 38 | } 39 | 40 | @Override 41 | public int getLayout() { 42 | return R.layout.pd_item_common; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/FuncItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.graphics.Color; 4 | import android.widget.ImageView; 5 | 6 | import tech.linjiang.pandora.core.R; 7 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 8 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 9 | 10 | /** 11 | * Created by linjiang on 2019/3/4. 12 | */ 13 | 14 | public class FuncItem extends BaseItem { 15 | 16 | private int icon; 17 | private boolean isSelected; 18 | public FuncItem(int icon, String data) { 19 | super(data); 20 | this.icon = icon; 21 | } 22 | 23 | public void setSelected(boolean selected) { 24 | isSelected = selected; 25 | } 26 | 27 | @Override 28 | public void onBinding(int position, UniversalAdapter.ViewPool pool, String data) { 29 | pool.setImageResource(R.id.icon, icon).setText(R.id.title, data); 30 | ImageView imageView = pool.getView(R.id.icon); 31 | imageView.setColorFilter(isSelected ? 0xffEB346E : Color.TRANSPARENT); 32 | pool.setTextColor(R.id.title, isSelected ? 0xffEB346E : Color.BLACK); 33 | } 34 | 35 | @Override 36 | public int getLayout() { 37 | return R.layout.pd_item_func; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/GridItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.graphics.Color; 4 | import android.graphics.Typeface; 5 | import android.text.TextUtils; 6 | import android.widget.TextView; 7 | 8 | import tech.linjiang.pandora.core.R; 9 | import tech.linjiang.pandora.database.Column; 10 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 11 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 12 | import tech.linjiang.pandora.util.ViewKnife; 13 | 14 | /** 15 | * Created by linjiang on 03/06/2018. 16 | */ 17 | 18 | public class GridItem extends BaseItem { 19 | 20 | public boolean isColumnName; 21 | private boolean isPrimaryKey; 22 | public String primaryKeyValue; 23 | public String columnName; 24 | 25 | public void setIsPrimaryKey() { 26 | isPrimaryKey = true; 27 | } 28 | 29 | public boolean isEnable() { 30 | return !isColumnName && !isPrimaryKey && !Column.ROW_ID.equals(columnName); 31 | } 32 | 33 | 34 | public GridItem(String columnValue, String primaryKeyValue, String columnName) { 35 | super(columnValue); 36 | this.primaryKeyValue = primaryKeyValue; 37 | this.columnName = columnName; 38 | } 39 | 40 | public GridItem(String data, boolean isColumnName) { 41 | super(data); 42 | this.isColumnName = isColumnName; 43 | } 44 | 45 | @Override 46 | public void onBinding(int position, UniversalAdapter.ViewPool pool, String data) { 47 | ((TextView) pool.getView(R.id.gird_text)).setTypeface(null, 48 | TextUtils.isEmpty(data) ? Typeface.ITALIC : Typeface.NORMAL); 49 | ((TextView) pool.getView(R.id.gird_text)).setTextColor( 50 | TextUtils.isEmpty(data) ? ViewKnife.getColor(R.color.pd_label) : Color.BLACK); 51 | pool.setText(R.id.gird_text, TextUtils.isEmpty(data) ? "NULL" : data); 52 | pool.setBackgroundColor(R.id.gird_text, 53 | !isEnable() ? ViewKnife.getColor(R.color.pd_item_key) : Color.WHITE); 54 | } 55 | 56 | @Override 57 | public int getLayout() { 58 | return R.layout.pd_item_table_cell; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/KeyEditItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.text.Editable; 4 | import android.text.TextWatcher; 5 | import android.view.View; 6 | import android.widget.EditText; 7 | 8 | import tech.linjiang.pandora.core.R; 9 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 10 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 11 | import tech.linjiang.pandora.ui.view.ExtraEditTextView; 12 | 13 | /** 14 | * Created by linjiang on 07/06/2018. 15 | */ 16 | 17 | public class KeyEditItem extends BaseItem { 18 | 19 | public boolean editable = true; 20 | public String hint; 21 | 22 | /** 23 | * 24 | * @param disable if can edit 25 | * @param data [0]: key [1]: value 26 | * @param hint hint of editText 27 | */ 28 | public KeyEditItem(boolean disable, String[] data, String hint) { 29 | super(data); 30 | this.editable = !disable; 31 | this.hint = hint; 32 | } 33 | 34 | public KeyEditItem(boolean disable, String[] data) { 35 | this(disable, data, null); 36 | } 37 | 38 | @Override 39 | public void onBinding(int position, UniversalAdapter.ViewPool pool, String[] data) { 40 | // must first call the following two lines of code 41 | ((ExtraEditTextView)pool.getView(R.id.item_edit)).clearTextChangedListeners(); 42 | ((EditText)pool.getView(R.id.item_edit)).addTextChangedListener(watcher); 43 | pool 44 | .setText(R.id.item_key, data[0]) 45 | .setText(R.id.item_edit, data[1]); 46 | ((EditText)pool.getView(R.id.item_edit)).setHint(hint); 47 | 48 | 49 | pool.getView(R.id.item_value).setVisibility(View.GONE); 50 | pool.getView(R.id.item_edit).setVisibility(View.VISIBLE); 51 | pool.getView(R.id.item_edit).setEnabled(editable); 52 | if (editable) { 53 | ((EditText)pool.getView(R.id.item_edit)).setSingleLine(true); 54 | } else { 55 | ((EditText)pool.getView(R.id.item_edit)).setSingleLine(false); 56 | } 57 | } 58 | 59 | @Override 60 | public int getLayout() { 61 | return R.layout.pd_item_key_value; 62 | } 63 | 64 | private TextWatcher watcher = new TextWatcher() { 65 | @Override 66 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 67 | 68 | } 69 | 70 | @Override 71 | public void onTextChanged(CharSequence s, int start, int before, int count) { 72 | 73 | } 74 | 75 | @Override 76 | public void afterTextChanged(Editable s) { 77 | if (data != null && data.length >= 2) { 78 | data[1] = s.toString(); 79 | } 80 | } 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/KeyValueItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.graphics.Color; 4 | import android.text.TextUtils; 5 | import android.view.Gravity; 6 | import android.view.View; 7 | 8 | import tech.linjiang.pandora.core.R; 9 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 10 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 11 | import tech.linjiang.pandora.util.ViewKnife; 12 | 13 | /** 14 | * Created by linjiang on 05/06/2018. 15 | */ 16 | 17 | public class KeyValueItem extends BaseItem { 18 | public boolean isTitle; 19 | public boolean clickable; 20 | private String prefix; 21 | 22 | 23 | public KeyValueItem(String[] data) { 24 | super(data); 25 | } 26 | 27 | public KeyValueItem(String[] data, boolean isTitle) { 28 | super(data); 29 | this.isTitle = isTitle; 30 | } 31 | 32 | public KeyValueItem(String[] data, boolean isTitle, boolean clickable) { 33 | super(data); 34 | this.isTitle = isTitle; 35 | this.clickable = clickable; 36 | } 37 | 38 | public KeyValueItem(String[] data, boolean isTitle, boolean clickable, String prefix) { 39 | super(data); 40 | this.isTitle = isTitle; 41 | this.clickable = clickable; 42 | this.prefix = prefix; 43 | } 44 | 45 | 46 | @Override 47 | public void onBinding(int position, UniversalAdapter.ViewPool pool, String[] data) { 48 | pool.setVisibility(R.id.item_prefix, TextUtils.isEmpty(prefix) ? View.GONE : View.VISIBLE); 49 | pool.setText(R.id.item_prefix, prefix); 50 | pool 51 | .setTextGravity(R.id.item_key, isTitle ? Gravity.CENTER : Gravity.CENTER_VERTICAL) 52 | .setText(R.id.item_key, data[0]) 53 | .setTextGravity(R.id.item_value, 54 | isTitle ? Gravity.CENTER : Gravity.CENTER_VERTICAL) 55 | .setBackgroundColor(R.id.item_value, 56 | isTitle ? ViewKnife.getColor(R.color.pd_item_key) : Color.WHITE) 57 | .setText(R.id.item_value, data[1]); 58 | 59 | pool.getView(R.id.item_value).setVisibility(View.VISIBLE); 60 | pool.getView(R.id.item_edit).setVisibility(View.GONE); 61 | pool.setVisibility(R.id.item_arrow, clickable ? View.VISIBLE : View.INVISIBLE); 62 | } 63 | 64 | @Override 65 | public int getLayout() { 66 | return R.layout.pd_item_key_value; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/NameArrowItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.view.View; 4 | 5 | import tech.linjiang.pandora.core.R; 6 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 7 | 8 | /** 9 | * Created by linjiang on 2018/7/24. 10 | */ 11 | 12 | public class NameArrowItem extends NameItem { 13 | private String arrowValue; 14 | 15 | public NameArrowItem(String data, String arrowValue) { 16 | super(data); 17 | this.arrowValue = arrowValue; 18 | } 19 | 20 | @Override 21 | public void onBinding(int position, UniversalAdapter.ViewPool pool, String data) { 22 | super.onBinding(position, pool, data); 23 | pool.setVisibility(R.id.common_item_arrow, View.VISIBLE).setText(R.id.common_item_arrow, arrowValue); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/NameItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.view.View; 4 | 5 | import tech.linjiang.pandora.core.R; 6 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 7 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 8 | 9 | /** 10 | * Created by linjiang on 03/06/2018. 11 | */ 12 | 13 | public class NameItem extends BaseItem { 14 | 15 | 16 | public NameItem(String data) { 17 | super(data); 18 | } 19 | 20 | @Override 21 | public void onBinding(int position, UniversalAdapter.ViewPool pool, String data) { 22 | pool 23 | .setVisibility(R.id.common_item_info, View.GONE) 24 | .setVisibility(R.id.common_item_arrow, View.GONE) 25 | .setText(R.id.common_item_title, data); 26 | } 27 | 28 | @Override 29 | public int getLayout() { 30 | return R.layout.pd_item_common; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/NetItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.graphics.Color; 4 | import android.text.TextUtils; 5 | 6 | import java.util.Locale; 7 | 8 | import tech.linjiang.pandora.core.R; 9 | import tech.linjiang.pandora.cache.Summary; 10 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 11 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 12 | import tech.linjiang.pandora.util.Utils; 13 | 14 | /** 15 | * Created by linjiang on 2018/6/23. 16 | */ 17 | 18 | public class NetItem extends BaseItem

{ 19 | 20 | public NetItem(Summary data) { 21 | super(data); 22 | } 23 | 24 | @Override 25 | public void onBinding(int position, UniversalAdapter.ViewPool pool, Summary data) { 26 | if (position % 2 == 0) { 27 | pool.itemView.setBackgroundResource(R.color.pd_item_bg); 28 | } else { 29 | pool.itemView.setBackgroundColor(Color.TRANSPARENT); 30 | } 31 | 32 | boolean done = data.status != 0; 33 | pool.setImageResource(R.id.item_net_status, !done ? R.drawable.pd_transform : 34 | (data.status == 1 ? R.drawable.pd_error : R.drawable.pd_done)); 35 | 36 | pool.setTextColor(R.id.item_net_url, done && data.code > 0 && data.code != 200 ? Color.BLUE : Color.BLACK); 37 | pool.setText(R.id.item_net_url, data.url) 38 | .setText(R.id.item_net_host, data.host) 39 | .setText(R.id.item_net_info, 40 | String.format(Locale.getDefault(), "%s %s %s%s%s", 41 | Utils.millis2String(data.start_time, Utils.HHMMSS), 42 | data.method, 43 | done && data.code > 0 ? String.valueOf(data.code) + " " : "", 44 | (done && data.response_size > 0) 45 | ? Utils.formatSize(data.response_size) + " " : "", 46 | done && data.end_time > 0 && data.start_time > 0 47 | ? String.valueOf(data.end_time - data.start_time) + "ms" : "")); 48 | 49 | if (done) { 50 | pool.setCompoundDrawableLeft(R.id.item_net_url, isImage(data.response_content_type) ? R.drawable.pd_image : 0); 51 | } else { 52 | pool.setCompoundDrawableLeft(R.id.item_net_url, 0); 53 | } 54 | } 55 | 56 | @Override 57 | public int getLayout() { 58 | return R.layout.pd_item_net; 59 | } 60 | 61 | private boolean isImage(String contentType) { 62 | return !TextUtils.isEmpty(contentType) && contentType.contains("image"); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/OptionItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import tech.linjiang.pandora.core.R; 4 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 5 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 6 | 7 | /** 8 | * Created by linjiang on 2018/6/20. 9 | */ 10 | 11 | public class OptionItem extends BaseItem { 12 | public OptionItem(String data) { 13 | super(data); 14 | } 15 | 16 | @Override 17 | public void onBinding(int position, UniversalAdapter.ViewPool pool, String data) { 18 | pool.setText(R.id.item_option_btn, data); 19 | } 20 | 21 | @Override 22 | public int getLayout() { 23 | return R.layout.pd_item_option; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/RouteItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.view.View; 4 | 5 | import tech.linjiang.pandora.core.R; 6 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 7 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 8 | 9 | /** 10 | * Created by linjiang on 2019/1/13. 11 | */ 12 | 13 | public class RouteItem extends BaseItem { 14 | private String simpleName; 15 | private Callback callback; 16 | 17 | public RouteItem(String data, Callback callback) { 18 | super(data); 19 | this.callback = callback; 20 | simpleName = data.substring(data.lastIndexOf(".") + 1); 21 | } 22 | 23 | @Override 24 | public void onBinding(int position, UniversalAdapter.ViewPool pool, final String data) { 25 | pool.setText(R.id.common_item_title, simpleName) 26 | .setText(R.id.common_item_info, data); 27 | pool.getView(R.id.go).setOnClickListener(new View.OnClickListener() { 28 | @Override 29 | public void onClick(View v) { 30 | if (callback != null) { 31 | callback.onClick(simpleName, data, false); 32 | } 33 | } 34 | }); 35 | pool.getView(R.id.param).setOnClickListener(new View.OnClickListener() { 36 | @Override 37 | public void onClick(View v) { 38 | if (callback != null) { 39 | callback.onClick(simpleName, data, true); 40 | } 41 | } 42 | }); 43 | } 44 | 45 | @Override 46 | public int getLayout() { 47 | return R.layout.pd_item_route; 48 | } 49 | 50 | public interface Callback { 51 | void onClick(String simpleName, String clazz, boolean needParam); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/SPItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * Created by linjiang on 05/06/2018. 7 | */ 8 | 9 | public class SPItem extends NameItem { 10 | public File descriptor; 11 | 12 | public SPItem(String data, File descriptor) { 13 | super(data); 14 | this.descriptor = descriptor; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/TitleItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import tech.linjiang.pandora.core.R; 4 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 5 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 6 | 7 | /** 8 | * Created by linjiang on 03/06/2018. 9 | */ 10 | 11 | public class TitleItem extends BaseItem { 12 | public TitleItem(String data) { 13 | super(data); 14 | } 15 | 16 | @Override 17 | public void onBinding(int position, UniversalAdapter.ViewPool pool, String data) { 18 | pool.setText(R.id.item_title_id, data); 19 | } 20 | 21 | @Override 22 | public int getLayout() { 23 | return R.layout.pd_item_title; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/ViewAttrItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.graphics.Color; 4 | import android.view.Gravity; 5 | import android.view.View; 6 | 7 | import tech.linjiang.pandora.core.R; 8 | import tech.linjiang.pandora.inspector.model.Attribute; 9 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 10 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 11 | 12 | /** 13 | * Created by linjiang on 2018/6/20. 14 | */ 15 | 16 | public class ViewAttrItem extends BaseItem { 17 | 18 | private boolean canEdit; 19 | 20 | public ViewAttrItem(Attribute data) { 21 | super(data); 22 | canEdit = data.attrType != Attribute.Edit.NORMAL; 23 | } 24 | 25 | @Override 26 | public void onBinding(int position, UniversalAdapter.ViewPool pool, Attribute data) { 27 | pool 28 | .setTextGravity(R.id.item_key, Gravity.CENTER_VERTICAL) 29 | .setText(R.id.item_key, data.attrName) 30 | .setTextGravity(R.id.item_value, 31 | Gravity.CENTER_VERTICAL) 32 | .setBackgroundColor(R.id.item_value, 33 | Color.WHITE) 34 | .setText(R.id.item_value, data.attrValue); 35 | 36 | pool.getView(R.id.item_value).setVisibility(View.VISIBLE); 37 | pool.getView(R.id.item_edit).setVisibility(View.GONE); 38 | pool.getView(R.id.item_arrow).setVisibility(canEdit ? View.VISIBLE : View.INVISIBLE); 39 | } 40 | 41 | @Override 42 | public int getLayout() { 43 | return R.layout.pd_item_key_value; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/item/ViewItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.item; 2 | 3 | import android.graphics.Color; 4 | import androidx.core.view.ViewCompat; 5 | import android.view.View; 6 | 7 | import tech.linjiang.pandora.core.R; 8 | import tech.linjiang.pandora.ui.recyclerview.BaseItem; 9 | import tech.linjiang.pandora.ui.recyclerview.UniversalAdapter; 10 | import tech.linjiang.pandora.util.ViewKnife; 11 | 12 | /** 13 | * Created by linjiang on 2018/7/22. 14 | */ 15 | 16 | public class ViewItem extends BaseItem { 17 | public boolean selected; 18 | public boolean related; 19 | 20 | public ViewItem(View data) { 21 | super(data); 22 | } 23 | 24 | public ViewItem(View data, boolean selected, boolean related) { 25 | super(data); 26 | this.selected = selected; 27 | this.related = related; 28 | } 29 | 30 | @Override 31 | public void onBinding(int position, UniversalAdapter.ViewPool pool, View data) { 32 | pool.setText(R.id.view_name_title, data.getClass().getSimpleName()) 33 | .setText(R.id.view_name_subtitle, ViewKnife.getIdString(data)); 34 | if (selected) { 35 | pool.getView(R.id.view_name_wrapper).setBackgroundColor(ViewKnife.getColor(R.color.pd_blue)); 36 | pool.setTextColor(R.id.view_name_title, Color.WHITE) 37 | .setTextColor(R.id.view_name_subtitle, Color.WHITE); 38 | } else { 39 | ViewCompat.setBackground(pool.getView(R.id.view_name_wrapper), 40 | ViewKnife.getDrawable(related ? R.drawable.pd_shape_btn_bg_related : R.drawable.pd_shape_btn_bg)); 41 | pool.setTextColor(R.id.view_name_title, 0xff000000) 42 | .setTextColor(R.id.view_name_subtitle, ViewKnife.getColor(R.color.pd_label_dark)); 43 | } 44 | } 45 | 46 | @Override 47 | public int getLayout() { 48 | return R.layout.pd_item_view_name; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/recyclerview/BaseItem.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.recyclerview; 2 | 3 | import androidx.annotation.LayoutRes; 4 | 5 | /** 6 | * Created by linjiang on 03/06/2018. 7 | */ 8 | 9 | public abstract class BaseItem { 10 | 11 | public T data; 12 | 13 | public BaseItem(T data) { 14 | this.data = data; 15 | } 16 | 17 | public abstract void onBinding(int position, UniversalAdapter.ViewPool pool, T data); 18 | 19 | public abstract @LayoutRes int getLayout(); 20 | 21 | private Object tag; 22 | 23 | public final BaseItem setTag(Object tag) { 24 | this.tag = tag; 25 | return this; 26 | } 27 | 28 | public final Object getTag() { 29 | return tag; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/view/ExtraEditTextView.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.view; 2 | 3 | import android.content.Context; 4 | import android.text.TextWatcher; 5 | import android.util.AttributeSet; 6 | import android.widget.EditText; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Created by linjiang on 07/06/2018. 12 | */ 13 | 14 | public class ExtraEditTextView extends EditText { 15 | private ArrayList mListeners = null; 16 | 17 | 18 | public ExtraEditTextView(Context context) { 19 | this(context, null); 20 | } 21 | 22 | public ExtraEditTextView(Context context, AttributeSet attrs) { 23 | this(context, attrs, 0); 24 | } 25 | 26 | public ExtraEditTextView(Context context, AttributeSet attrs, int defStyleAttr) { 27 | super(context, attrs, defStyleAttr); 28 | } 29 | 30 | @Override 31 | public void addTextChangedListener(TextWatcher watcher) { 32 | if (mListeners == null) { 33 | mListeners = new ArrayList<>(); 34 | } 35 | mListeners.add(watcher); 36 | 37 | super.addTextChangedListener(watcher); 38 | } 39 | 40 | @Override 41 | public void removeTextChangedListener(TextWatcher watcher) { 42 | if (mListeners != null) { 43 | int i = mListeners.indexOf(watcher); 44 | if (i >= 0) { 45 | mListeners.remove(i); 46 | } 47 | } 48 | 49 | super.removeTextChangedListener(watcher); 50 | } 51 | 52 | public void clearTextChangedListeners() { 53 | if (mListeners != null) { 54 | for (TextWatcher watcher : mListeners) { 55 | super.removeTextChangedListener(watcher); 56 | } 57 | 58 | mListeners.clear(); 59 | mListeners = null; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/view/MenuRecyclerView.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.view; 2 | 3 | import android.content.Context; 4 | import androidx.annotation.Nullable; 5 | import androidx.recyclerview.widget.RecyclerView; 6 | import android.util.AttributeSet; 7 | import android.view.ContextMenu; 8 | import android.view.View; 9 | 10 | /** 11 | * Created by linjiang on 07/06/2018. 12 | */ 13 | 14 | public class MenuRecyclerView extends RecyclerView { 15 | public MenuRecyclerView(Context context) { 16 | super(context); 17 | } 18 | 19 | public MenuRecyclerView(Context context, @Nullable AttributeSet attrs) { 20 | super(context, attrs); 21 | } 22 | 23 | public MenuRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { 24 | super(context, attrs, defStyle); 25 | } 26 | 27 | private RvContextMenuInfo contextMenuInfo; 28 | 29 | @Override 30 | protected ContextMenu.ContextMenuInfo getContextMenuInfo() { 31 | return contextMenuInfo; 32 | } 33 | 34 | @Override 35 | public boolean showContextMenuForChild(View originalView) { 36 | // only valid for the direct child 37 | if (indexOfChild(originalView) == -1) { 38 | return false; 39 | } 40 | final int position = getChildAdapterPosition(originalView); 41 | if (position >= 0) { 42 | final long itemId = getAdapter().getItemId(position); 43 | contextMenuInfo = new RvContextMenuInfo(originalView, position, itemId); 44 | return super.showContextMenuForChild(originalView); 45 | } 46 | return false; 47 | } 48 | 49 | public class RvContextMenuInfo implements ContextMenu.ContextMenuInfo { 50 | 51 | public RvContextMenuInfo(View targetView, int position, long id) { 52 | this.targetView = targetView; 53 | this.position = position; 54 | this.id = id; 55 | } 56 | 57 | public View targetView; 58 | public int position; 59 | public long id; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/view/MultiRvLayout.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.view; 2 | 3 | import android.content.Context; 4 | import androidx.annotation.Nullable; 5 | import androidx.core.view.NestedScrollingChild; 6 | import android.util.AttributeSet; 7 | import android.widget.LinearLayout; 8 | 9 | /** 10 | * Created by linjiang on 2018/7/23. 11 | *

12 | * because behavior only support one scroll-view, we wrap multi scroll views just look like that. 13 | */ 14 | 15 | public class MultiRvLayout extends LinearLayout implements NestedScrollingChild { 16 | 17 | public MultiRvLayout(Context context) { 18 | super(context); 19 | } 20 | 21 | public MultiRvLayout(Context context, @Nullable AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | public MultiRvLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 26 | super(context, attrs, defStyleAttr); 27 | } 28 | 29 | @Override 30 | public boolean isNestedScrollingEnabled() { 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/ui/view/SquareLayout.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.ui.view; 2 | 3 | import android.content.Context; 4 | import androidx.annotation.Nullable; 5 | import android.util.AttributeSet; 6 | import android.widget.LinearLayout; 7 | 8 | /** 9 | * Created by linjiang on 2019/3/4. 10 | */ 11 | 12 | public class SquareLayout extends LinearLayout { 13 | public SquareLayout(Context context) { 14 | super(context); 15 | } 16 | 17 | public SquareLayout(Context context, @Nullable AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | 21 | public SquareLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 22 | super(context, attrs, defStyleAttr); 23 | } 24 | 25 | @Override 26 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 27 | super.onMeasure(heightMeasureSpec, heightMeasureSpec); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/util/FormatUtil.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.util; 2 | 3 | import android.text.TextUtils; 4 | import android.util.Pair; 5 | 6 | import org.json.JSONArray; 7 | import org.json.JSONException; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import okhttp3.Headers; 13 | 14 | /** 15 | * Created by linjiang on 2018/6/21. 16 | */ 17 | 18 | public class FormatUtil { 19 | 20 | 21 | public static String formatHeaders(Headers headers) { 22 | JSONArray array = new JSONArray(); 23 | for (int i = 0, size = headers.size(); i < size; i++) { 24 | array.put(new JSONArray().put(headers.name(i)).put(headers.value(i))); 25 | } 26 | if (array.length() > 0) { 27 | return array.toString(); 28 | } else { 29 | return null; 30 | } 31 | } 32 | 33 | public static List> parseHeaders(String headers) { 34 | List> headerList = new ArrayList<>(); 35 | if (!TextUtils.isEmpty(headers)) { 36 | try { 37 | JSONArray array = new JSONArray(headers); 38 | for (int i = 0; i < array.length(); i++) { 39 | Pair header = new Pair<>( 40 | array.getJSONArray(i).getString(0), array.getJSONArray(i).getString(1)); 41 | headerList.add(header); 42 | } 43 | } catch (JSONException e) { 44 | e.printStackTrace(); 45 | } 46 | } 47 | return headerList; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/util/Reflect28Util.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.util; 2 | 3 | import android.os.Build; 4 | 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * Created by linjiang on 2019/3/5. 10 | */ 11 | 12 | class Reflect28Util { 13 | 14 | static { 15 | if (Build.VERSION.SDK_INT >= 28) { 16 | try { 17 | Class classClazz = Class.class; 18 | // light greyList 19 | Field classLoaderField = classClazz.getDeclaredField("classLoader"); 20 | classLoaderField.setAccessible(true); 21 | classLoaderField.set(Reflect28Util.class, null); 22 | } catch (Exception e) { 23 | e.printStackTrace(); 24 | } 25 | } 26 | } 27 | 28 | public static Class forName(String className) throws ClassNotFoundException { 29 | return Class.forName(className); 30 | } 31 | 32 | public static Field getDeclaredField(Class clz, String name) throws NoSuchFieldException { 33 | return clz.getDeclaredField(name); 34 | } 35 | 36 | public static Method getDeclaredMethod(Class clz, String name, Class... parameterType) 37 | throws NoSuchMethodException { 38 | return clz.getDeclaredMethod(name, parameterType); 39 | } 40 | 41 | 42 | } -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/util/SensorDetector.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.util; 2 | 3 | import android.content.Context; 4 | import android.hardware.Sensor; 5 | import android.hardware.SensorEvent; 6 | import android.hardware.SensorEventListener; 7 | import android.hardware.SensorManager; 8 | 9 | /** 10 | * Created by linjiang on 2019/3/5. 11 | */ 12 | 13 | public class SensorDetector implements SensorEventListener { 14 | 15 | private static long lastCheckTime; 16 | private static float[] lastXyz = new float[3]; 17 | private Callback callback; 18 | 19 | public SensorDetector(Callback callback) { 20 | if (callback != null) { 21 | register(); 22 | this.callback = callback; 23 | } 24 | } 25 | 26 | @Override 27 | public void onSensorChanged(SensorEvent event) { 28 | if (Config.getSHAKE_SWITCH()) { 29 | if (event.sensor.getType() == 1) { 30 | // app-window will only receive event at the top 31 | if (checkIfShake( 32 | event.values[0], 33 | event.values[1], 34 | event.values[2])) { 35 | Utils.cancelTask(task); 36 | Utils.postDelayed(task, 150); 37 | } 38 | } 39 | } 40 | } 41 | 42 | @Override 43 | public void onAccuracyChanged(Sensor sensor, int accuracy) { 44 | 45 | } 46 | 47 | 48 | private void register() { 49 | try { 50 | SensorManager manager = (SensorManager) Utils.getContext().getSystemService(Context.SENSOR_SERVICE); 51 | Sensor sensor = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 52 | manager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL); 53 | } catch (Throwable t) { 54 | t.printStackTrace(); 55 | } 56 | } 57 | 58 | public void unRegister() { 59 | try { 60 | SensorManager manager = (SensorManager) Utils.getContext().getSystemService(Context.SENSOR_SERVICE); 61 | Sensor sensor = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 62 | manager.unregisterListener(this, sensor); 63 | } catch (Throwable t) { 64 | t.printStackTrace(); 65 | } 66 | } 67 | 68 | private Runnable task = new Runnable() { 69 | @Override 70 | public void run() { 71 | if (callback != null) { 72 | callback.shakeValid(); 73 | } 74 | } 75 | }; 76 | 77 | 78 | private static boolean checkIfShake(float x, float y, float z) { 79 | long currentTime = System.currentTimeMillis(); 80 | long diffTime = currentTime - lastCheckTime; 81 | if (diffTime < 100) { 82 | return false; 83 | } 84 | lastCheckTime = currentTime; 85 | float deltaX = x - lastXyz[0]; 86 | float deltaY = y - lastXyz[1]; 87 | float deltaZ = z - lastXyz[2]; 88 | lastXyz[0] = x; 89 | lastXyz[1] = y; 90 | lastXyz[2] = z; 91 | int delta = (int) (Math.sqrt(deltaX * deltaX 92 | + deltaY * deltaY + deltaZ * deltaZ) / diffTime * 10000); 93 | return delta > Config.getSHAKE_THRESHOLD(); 94 | } 95 | 96 | public interface Callback { 97 | void shakeValid(); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /pandora-core/src/main/java/tech/linjiang/pandora/util/SimpleTask.java: -------------------------------------------------------------------------------- 1 | package tech.linjiang.pandora.util; 2 | 3 | import android.os.AsyncTask; 4 | import android.util.Log; 5 | 6 | import java.lang.ref.WeakReference; 7 | 8 | import tech.linjiang.pandora.core.R; 9 | 10 | /** 11 | * Created by linjiang on 03/06/2018. 12 | */ 13 | public class SimpleTask extends AsyncTask { 14 | 15 | private static final String TAG = "SimpleTask"; 16 | 17 | // On some low memory phones, the GC is very frequent, which causes the object to be released 18 | // private WeakReference> callbackRef; 19 | private Callback callback; 20 | 21 | private Callback getCallback() { 22 | // if (callbackRef != null) { 23 | // Callback callback = callbackRef.get(); 24 | // if (callback != null) { 25 | // return callback; 26 | // } 27 | // } 28 | // return null; 29 | return callback; 30 | } 31 | 32 | public SimpleTask(Callback callback) { 33 | // this.callbackRef = new WeakReference<>(callback); 34 | this.callback = callback; 35 | } 36 | 37 | @Override 38 | protected final void onPreExecute() { 39 | 40 | } 41 | 42 | @Override 43 | protected final Result doInBackground(Params[] params) { 44 | if (getCallback() != null) { 45 | try { 46 | return getCallback().doInBackground(params); 47 | } catch (Throwable t) { 48 | t.printStackTrace(); 49 | } 50 | } else { 51 | Log.w(TAG, "doInBackground: getCallback() == null"); 52 | } 53 | return null; 54 | } 55 | 56 | @Override 57 | protected final void onPostExecute(Result result) { 58 | if (getCallback() != null) { 59 | try { 60 | getCallback().onPostExecute(result); 61 | } catch (Throwable t) { 62 | t.printStackTrace(); 63 | } 64 | // if (callbackRef != null) { 65 | // callbackRef.clear(); 66 | // callbackRef = null; 67 | // } 68 | callback = null; 69 | } else { 70 | Log.w(TAG, "onPostExecute: getCallback() == null"); 71 | } 72 | } 73 | 74 | public interface Callback { 75 | K doInBackground(T[] params); 76 | void onPostExecute(K result); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/anim/slide_in_right_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/anim/slide_out_right_.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_add.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_bug.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_close.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_collapse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_collapse.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_config.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_delete.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_disk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_disk.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_done.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_drag.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_edit.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_error.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_expand.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_grid.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_help.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_history.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_image.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_info.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_layer.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_map.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_more.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_network.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_play.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_right.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_ruler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_ruler.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_search.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_select.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_shadow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_shadow_left.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_transform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_transform.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_up_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_up_down.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable-xxhdpi/pd_windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable-xxhdpi/pd_windows.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable/pd_shadow_131124.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable/pd_shadow_131124.9.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable/pd_shadow_23354.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whataa/pandora/c9eb58272ccb3ff98994d162b5e3a311cfa1a9e3/pandora-core/src/main/res/drawable/pd_shadow_23354.9.png -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable/pd_shape_btn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/drawable/pd_shape_btn_bg_related.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/layout/pd_item_checkbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/layout/pd_item_common.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 21 | 22 | 32 | 33 | 44 | 45 | 46 | 56 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/layout/pd_item_exception.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/layout/pd_item_func.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 21 | 22 | 31 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/layout/pd_item_hierachy.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 21 | 22 | 35 | 36 | 48 | 49 | 50 | 58 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/layout/pd_item_key_value.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 21 | 22 | 26 | 27 | 37 | 38 | 42 | 55 | 73 | 82 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/layout/pd_item_net.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 23 | 24 | 29 | 30 | 42 | 43 | 55 | 56 | 66 | 67 | 68 | 74 | -------------------------------------------------------------------------------- /pandora-core/src/main/res/layout/pd_item_option.xml: -------------------------------------------------------------------------------- 1 | 2 |