├── README.md ├── code └── Sample │ ├── .gitignore │ ├── .idea │ ├── .name │ ├── compiler.xml │ ├── copyright │ │ └── profiles_settings.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml │ ├── app │ ├── .gitignore │ ├── build.gradle │ ├── libs │ │ ├── android-support-v4.jar │ │ └── connectionclass-1.0.1.jar │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── sample │ │ │ └── ApplicationTest.java │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ ├── DroidSansFallback.ttf │ │ ├── core3.mp3 │ │ ├── fontGqqV2.ttf │ │ ├── kemar_L.bin │ │ ├── simfang.ttf │ │ └── www │ │ │ └── index.html │ │ ├── java │ │ ├── cellText │ │ │ ├── CellTextView.java │ │ │ ├── ColorTextCell.java │ │ │ ├── EmoCell.java │ │ │ ├── EmoObjectPool.java │ │ │ ├── ReMeasureableLayout.java │ │ │ ├── TextCell.java │ │ │ ├── UiElementFixedCache.java │ │ │ ├── UrlCell.java │ │ │ ├── UserNameCell.java │ │ │ └── touchbehavior │ │ │ │ ├── SingleClickBehavior.java │ │ │ │ ├── TouchAnalizer.java │ │ │ │ ├── TouchBehavior.java │ │ │ │ └── TouchBehaviorListener.java │ │ ├── com │ │ │ ├── android │ │ │ │ └── grafika │ │ │ │ │ └── gles │ │ │ │ │ ├── Drawable2d.java │ │ │ │ │ ├── EglCore.java │ │ │ │ │ ├── EglSurfaceBase.java │ │ │ │ │ ├── FlatShadedProgram.java │ │ │ │ │ ├── FullFrameRect.java │ │ │ │ │ ├── GeneratedTexture.java │ │ │ │ │ ├── GlUtil.java │ │ │ │ │ ├── OffscreenSurface.java │ │ │ │ │ ├── Sprite2d.java │ │ │ │ │ ├── Texture2dProgram.java │ │ │ │ │ └── WindowSurface.java │ │ │ ├── sample │ │ │ │ ├── App.java │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── Fragment │ │ │ │ │ ├── MyActivtiy.java │ │ │ │ │ └── MyFragment.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MediaProjectionDemo.java │ │ │ │ ├── MyLinearLayout.java │ │ │ │ ├── ScreenShotView.java │ │ │ │ ├── TEST │ │ │ │ │ ├── NewTestView.java │ │ │ │ │ └── TestView.java │ │ │ │ ├── animator │ │ │ │ │ └── AnimatorActivity.java │ │ │ │ ├── anr │ │ │ │ │ └── ANRTestActivity.java │ │ │ │ ├── architect │ │ │ │ │ ├── mvc │ │ │ │ │ │ ├── MVCActivity.java │ │ │ │ │ │ └── Model.java │ │ │ │ │ └── mvp │ │ │ │ │ │ ├── model │ │ │ │ │ │ ├── IModel.java │ │ │ │ │ │ └── Model.java │ │ │ │ │ │ ├── presenter │ │ │ │ │ │ └── Presenter.java │ │ │ │ │ │ └── view │ │ │ │ │ │ ├── IView.java │ │ │ │ │ │ └── MVPActivity.java │ │ │ │ ├── attr_style │ │ │ │ │ ├── AttrStyleActivity.java │ │ │ │ │ └── CustomView.java │ │ │ │ ├── audio │ │ │ │ │ ├── AudioActivity.java │ │ │ │ │ ├── BiquadFilter.java │ │ │ │ │ ├── Convolve.java │ │ │ │ │ ├── Delaunay.java │ │ │ │ │ ├── IR.java │ │ │ │ │ └── PlaySound.java │ │ │ │ ├── classLoader │ │ │ │ │ ├── ClassLoaderActivity.java │ │ │ │ │ └── getgameapp │ │ │ │ │ │ ├── GameAppListActivity.java │ │ │ │ │ │ ├── GetGameApp.java │ │ │ │ │ │ ├── MultiDexExtractor.java │ │ │ │ │ │ ├── MultiDexHelper.java │ │ │ │ │ │ └── ZipUtil.java │ │ │ │ ├── crashreport │ │ │ │ │ ├── CatchCrash.java │ │ │ │ │ └── TestCrashActivity.java │ │ │ │ ├── dialog │ │ │ │ │ ├── CustomDialog.java │ │ │ │ │ └── DialogActivity.java │ │ │ │ ├── glide │ │ │ │ │ ├── CustomCachingGlideModule.java │ │ │ │ │ ├── CustomLruBitmapPool.java │ │ │ │ │ ├── CustomLruResourceCache.java │ │ │ │ │ ├── GlideActivity.java │ │ │ │ │ └── GlideCircleTransform.java │ │ │ │ ├── hotfix │ │ │ │ │ └── dex │ │ │ │ │ │ ├── BugClass.java │ │ │ │ │ │ ├── HotFixActivity.java │ │ │ │ │ │ └── MultiDex.java │ │ │ │ ├── lifecycle │ │ │ │ │ ├── ActivityA.java │ │ │ │ │ ├── ActivityB.java │ │ │ │ │ └── RegActivityLifecycleCallback.java │ │ │ │ ├── multiprocess │ │ │ │ │ ├── ProcessActivity.java │ │ │ │ │ └── ProcessService.java │ │ │ │ ├── myTextView.java │ │ │ │ ├── network │ │ │ │ │ └── NetworkActivity.java │ │ │ │ ├── opengles │ │ │ │ │ ├── GLRenderer.java │ │ │ │ │ ├── GLSurf.java │ │ │ │ │ ├── OpenGLActivity.java │ │ │ │ │ └── riGraphicTools.java │ │ │ │ ├── performance │ │ │ │ │ ├── ActivityLifeCycleTimeUseTracker.java │ │ │ │ │ ├── ActivityLifeCycleTracker.java │ │ │ │ │ ├── ReflectHelper.java │ │ │ │ │ ├── TooDeepViewStackException.java │ │ │ │ │ ├── ViewHericacy.java │ │ │ │ │ └── ViewTreeTracer.java │ │ │ │ ├── plugin │ │ │ │ │ ├── activity │ │ │ │ │ │ ├── StubActivity.java │ │ │ │ │ │ ├── TargetActivity.java │ │ │ │ │ │ └── hook │ │ │ │ │ │ │ ├── AMSHookHelper.java │ │ │ │ │ │ │ ├── ActivityThreadHandlerCallback.java │ │ │ │ │ │ │ └── IActivityManagerHandler.java │ │ │ │ │ ├── pluginInstall.java │ │ │ │ │ └── pluginLoad.java │ │ │ │ ├── record │ │ │ │ │ ├── RecordActivity.java │ │ │ │ │ ├── RecordUtil.java │ │ │ │ │ └── ScreenCapture.java │ │ │ │ ├── res_confuse │ │ │ │ │ └── GetRes.java │ │ │ │ ├── sleep │ │ │ │ │ ├── SampleAlarmReceiver.java │ │ │ │ │ ├── SampleBootReceiver.java │ │ │ │ │ ├── SampleSchedulingService.java │ │ │ │ │ └── TestSleepActivity.java │ │ │ │ ├── statsUsage │ │ │ │ │ └── StatsUsageActivity.java │ │ │ │ └── web │ │ │ │ │ └── WebViewActivity.java │ │ │ └── tencent │ │ │ │ └── qqgamemi │ │ │ │ └── srp │ │ │ │ └── media │ │ │ │ └── newapi │ │ │ │ ├── InputSurface.java │ │ │ │ ├── MediaAudioEncoder.java │ │ │ │ ├── MediaEncoder.java │ │ │ │ ├── MediaMuxerWrapper.java │ │ │ │ ├── MediaScreenEncoder.java │ │ │ │ ├── MediaVideoEncoderBase.java │ │ │ │ ├── OutputSurface.java │ │ │ │ ├── ScreenRecorderManager.java │ │ │ │ ├── ScreenRecorderService.java │ │ │ │ └── TextureRender.java │ │ └── fonticon │ │ │ └── IconFontTextView.java │ │ ├── jniLibs │ │ └── apatch-debug.aar │ │ └── res │ │ ├── drawable │ │ ├── ic_launcher.png │ │ ├── startup_twinkle_animation.xml │ │ └── ui_selector_iconfont_textview_bg.xml │ │ ├── layout │ │ ├── activity_animator.xml │ │ ├── activity_anr_test.xml │ │ ├── activity_attr_style.xml │ │ ├── activity_audio.xml │ │ ├── activity_dialog.xml │ │ ├── activity_glide.xml │ │ ├── activity_hot_fix.xml │ │ ├── activity_list_view_activty.xml │ │ ├── activity_main.xml │ │ ├── activity_mvc.xml │ │ ├── activity_mvp.xml │ │ ├── activity_network.xml │ │ ├── activity_opengl.xml │ │ ├── activity_process.xml │ │ ├── activity_record.xml │ │ ├── activity_stats_usage.xml │ │ ├── activity_test_crash.xml │ │ ├── activity_test_sleep.xml │ │ ├── content_list_view_activty.xml │ │ ├── custom_dialog.xml │ │ ├── fragment.xml │ │ ├── layout_fragment.xml │ │ ├── media_projection.xml │ │ ├── rowlayout.xml │ │ └── webview_activity.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── startup_twinkle_animation4.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle └── doc ├── CBR、VBR、AVR.jpeg ├── MemoryProfile_Big_Texture2D.png ├── MemoryProfile_Memory_Leak.png ├── android_event_dispatch.png ├── android_framework.gif ├── android_meminfo.jpg ├── android_tech_mind_map.png ├── classloader.png ├── linux_sleep.gif ├── network_delay.png ├── sleep_diagram.gif ├── wake_diagram.gif ├── wake_type.jpg └── wake_type_and_sleep.jpg /code/Sample/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /code/Sample/.idea/.name: -------------------------------------------------------------------------------- 1 | Sample -------------------------------------------------------------------------------- /code/Sample/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /code/Sample/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /code/Sample/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /code/Sample/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /code/Sample/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /code/Sample/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /code/Sample/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /code/Sample/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 21 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.sample" 9 | minSdkVersion 19 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled true 17 | shrinkResources true 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | 21 | debug { 22 | minifyEnabled false 23 | shrinkResources false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | } 28 | 29 | repositories { 30 | flatDir { 31 | dirs android.sourceSets.main.jniLibs.srcDirs 32 | println android.sourceSets.main.jniLibs.srcDirs 33 | } 34 | } 35 | 36 | dependencies { 37 | compile fileTree(dir: 'libs', include: ['*.jar']) 38 | 39 | // compile 'moai.patch:apatch-debug:@aar' 40 | //compile 'com.android.support:multidex:1.0.1' 41 | //testCompile 'junit:junit:4.12' 42 | //compile 'com.android.support:appcompat-v7:23.1.1' 43 | //compile 'com.android.support:design:23.1.1' 44 | //compile 'com.android.support:support-v4:23.1.1' 45 | compile 'com.github.bumptech.glide:glide:3.6.1' 46 | compile 'com.github.anrwatchdog:anrwatchdog:1.3.+' 47 | compile 'com.kymjs.rxvolley:rxvolley:1.0.7' 48 | //compile 'com.android.support:appcompat-v7:23.4.0' 49 | } 50 | 51 | //apply plugin: 'patch-plugin' 52 | -------------------------------------------------------------------------------- /code/Sample/app/libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clarkehe/Android/ebb4a4427d6e4e9d37e896841aa93f9c6ee2b1f3/code/Sample/app/libs/android-support-v4.jar -------------------------------------------------------------------------------- /code/Sample/app/libs/connectionclass-1.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clarkehe/Android/ebb4a4427d6e4e9d37e896841aa93f9c6ee2b1f3/code/Sample/app/libs/connectionclass-1.0.1.jar -------------------------------------------------------------------------------- /code/Sample/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/fclarke/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /code/Sample/app/src/androidTest/java/com/sample/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.sample; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | > 40 | 43 | 47 | 50 | 51 | 54 | 55 | 56 | 57 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/assets/DroidSansFallback.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clarkehe/Android/ebb4a4427d6e4e9d37e896841aa93f9c6ee2b1f3/code/Sample/app/src/main/assets/DroidSansFallback.ttf -------------------------------------------------------------------------------- /code/Sample/app/src/main/assets/core3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clarkehe/Android/ebb4a4427d6e4e9d37e896841aa93f9c6ee2b1f3/code/Sample/app/src/main/assets/core3.mp3 -------------------------------------------------------------------------------- /code/Sample/app/src/main/assets/fontGqqV2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clarkehe/Android/ebb4a4427d6e4e9d37e896841aa93f9c6ee2b1f3/code/Sample/app/src/main/assets/fontGqqV2.ttf -------------------------------------------------------------------------------- /code/Sample/app/src/main/assets/kemar_L.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clarkehe/Android/ebb4a4427d6e4e9d37e896841aa93f9c6ee2b1f3/code/Sample/app/src/main/assets/kemar_L.bin -------------------------------------------------------------------------------- /code/Sample/app/src/main/assets/simfang.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clarkehe/Android/ebb4a4427d6e4e9d37e896841aa93f9c6ee2b1f3/code/Sample/app/src/main/assets/simfang.ttf -------------------------------------------------------------------------------- /code/Sample/app/src/main/assets/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 |

Test

25 | W3School 26 | W3School 27 | 28 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/ColorTextCell.java: -------------------------------------------------------------------------------- 1 | package cellText; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import android.graphics.Rect; 6 | import android.text.TextUtils; 7 | import android.util.Log; 8 | 9 | /** 10 | * 用来自定义颜色文字 11 | * @author alairzhang 12 | * 13 | */ 14 | public class ColorTextCell extends TextCell { 15 | /** 16 | * 17 | */ 18 | private static final long serialVersionUID = -7171491901412815564L; 19 | private boolean clickable = true; 20 | 21 | 22 | public ColorTextCell() { 23 | super(SIGN_NORMAL); 24 | } 25 | 26 | public ColorTextCell(int type) { 27 | super(type); 28 | } 29 | 30 | public ColorTextCell(int type, String text) { 31 | super(type, text); 32 | } 33 | 34 | private int textColor; 35 | private boolean bold; 36 | 37 | 38 | public void setTextColor(int textColor) { 39 | this.textColor = textColor; 40 | } 41 | 42 | @Override 43 | public boolean clickable() { 44 | boolean clickable = super.clickable(); 45 | return this.clickable && clickable; 46 | } 47 | 48 | public void setClickable(boolean clickable) { 49 | this.clickable = clickable; 50 | } 51 | 52 | public void setTextBold(boolean b) 53 | { 54 | this.bold=b; 55 | } 56 | 57 | @Override 58 | public void draw(Canvas canvas, Paint paint, int lineHeight, Rect rect, int textColor, int textColorLink) { 59 | if ((type & ColorTextCell.FLAG_TYPE_MASK) == ColorTextCell.SIGN_NORMAL 60 | || type == TextCell.SIGN_USER 61 | || type == TextCell.SIGN_URL 62 | || type == TextCell.SIGN_GOTO_DETAIL 63 | || type == TextCell.SIGN_FEED_OWNER) { 64 | paint.setColor(this.textColor); 65 | } else { 66 | paint.setColor(textColor); 67 | } 68 | boolean boldRestore = paint.isFakeBoldText(); 69 | if (bold) { // 仅当加粗时修改字体,用后还原 70 | paint.setFakeBoldText(bold); 71 | } 72 | int drawY = rect.top + (int) ((lineHeight - paint.descent() - paint.ascent()) / 2); 73 | 74 | String drawText = text; 75 | if (text.endsWith("\r\n")) { 76 | drawText = text.substring(0, text.length() - 2); 77 | } else if (text.endsWith("\n")) { 78 | drawText = text.substring(0, text.length() - 1); 79 | } 80 | 81 | if(!TextUtils.isEmpty(drawText)) { 82 | 83 | Log.d("ColorTextCell", "left" + rect.left); 84 | Log.d("ColorTextCell", "drawY" + drawY); 85 | 86 | canvas.drawText(drawText, rect.left, drawY, paint); 87 | } 88 | 89 | if (bold) { 90 | paint.setFakeBoldText(boldRestore); 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/EmoCell.java: -------------------------------------------------------------------------------- 1 | package cellText; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import android.graphics.Rect; 6 | import android.graphics.drawable.Drawable; 7 | import android.text.TextUtils; 8 | 9 | public class EmoCell extends TextCell { 10 | private static final long serialVersionUID = -3999833992135197771L; 11 | 12 | public Drawable emoDrawable; 13 | String emoCode; 14 | 15 | public EmoCell() { 16 | type = SIGN_NORMAL; 17 | } 18 | 19 | public EmoCell(String emoCode, Drawable drawable) { 20 | super(SIGN_NORMAL); 21 | this.emoCode = emoCode; 22 | this.emoDrawable = drawable; 23 | } 24 | public EmoCell(String text) { 25 | this.text = text; 26 | } 27 | 28 | @Override 29 | public boolean canBreak() { 30 | return false; 31 | } 32 | 33 | @Override 34 | public boolean isEmo() { 35 | return true; 36 | } 37 | 38 | @Override 39 | public String getText() { 40 | if (TextUtils.isEmpty(this.text)) { 41 | text = emoCode; 42 | } 43 | return text; 44 | } 45 | 46 | @Override 47 | public int getLength() { 48 | return 1; 49 | } 50 | 51 | public Drawable getEmoDrawable() { 52 | return emoDrawable; 53 | } 54 | 55 | @Override 56 | public float getWidth(Paint p) { 57 | Drawable d = getEmoDrawable(); 58 | return d == null ? 0 : d.getBounds().width(); 59 | } 60 | 61 | @Override 62 | public int getWidths(Paint p, int start, int maxCount, float[] widths) { 63 | Drawable d = getEmoDrawable(); 64 | widths[0] = d == null ? 0 : d.getBounds().width(); 65 | return 1; 66 | } 67 | 68 | @Override 69 | public int getHeight(Paint paint) { 70 | Drawable d = getEmoDrawable(); 71 | return d == null ? 0 : d.getBounds().height(); 72 | } 73 | 74 | @Override 75 | public void draw(Canvas canvas, Paint paint, int lineHeight, Rect rect, int textColor, int textColorLink) { 76 | Drawable d = getEmoDrawable(); 77 | if (d != null) { 78 | int x = rect.left; 79 | int y = rect.top-1 + (lineHeight - d.getBounds().height()) / 2; 80 | canvas.translate(x, y); 81 | d.draw(canvas); 82 | canvas.translate(-x, -y); 83 | } 84 | } 85 | 86 | @Override 87 | public void draw(Canvas canvas, Paint paint, int lineHeight, int textColor, int textColorLink, int breakTail) { 88 | Drawable d = getEmoDrawable(); 89 | if (d != null) { 90 | int x = rect.left; 91 | int y = rect.top-1 + (lineHeight - d.getBounds().height()) / 2; 92 | canvas.translate(x, y); 93 | d.draw(canvas); 94 | canvas.translate(-x, -y); 95 | } 96 | 97 | } 98 | 99 | @Override 100 | public TextCell copy() { 101 | return this; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/EmoObjectPool.java: -------------------------------------------------------------------------------- 1 | package cellText; 2 | 3 | import java.lang.ref.SoftReference; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | import android.graphics.drawable.Drawable; 7 | import android.os.Handler; 8 | import android.os.Looper; 9 | import android.util.SparseArray; 10 | 11 | public class EmoObjectPool { 12 | 13 | private static EmoObjectPool mInstance; 14 | private static Object lock = new Object(); 15 | private SparseArray defaultEmoList; 16 | 17 | private ConcurrentHashMap>> mEmoPool; 18 | 19 | /** 20 | * EmoObjectPool 单件 21 | * @return 22 | */ 23 | public static EmoObjectPool getInstance() { 24 | if (mInstance == null){ 25 | synchronized (lock) { 26 | if (mInstance == null) { 27 | mInstance = new EmoObjectPool(); 28 | } 29 | } 30 | } 31 | return mInstance; 32 | } 33 | 34 | private EmoObjectPool() { 35 | mEmoPool = new ConcurrentHashMap>>(); 36 | defaultEmoList = new SparseArray(); 37 | } 38 | 39 | public void refreshEmoDrawable(EmoCell cell, ReMeasureableLayout parent) { 40 | // if (cell == null) { 41 | // return; 42 | // } 43 | // 44 | // int bound = (int)cell.getWidth(null); 45 | // Drawable defaultDrawable = defaultEmoList.get(bound); 46 | // if (defaultDrawable == null) { 47 | // defaultDrawable = BaseApplication.getContext().getResources().getDrawable(R.drawable.qzone_icon_default_emoji); 48 | // defaultDrawable.setBounds(0, 0, bound, bound); 49 | // defaultEmoList.put(bound, defaultDrawable); 50 | // } 51 | // 52 | // if (cell.emoDrawable == defaultDrawable) { 53 | // loadEmoAsync(cell, cell.emoCode, bound, parent); 54 | // } 55 | } 56 | 57 | 58 | private Handler mainHandler = new Handler(Looper.getMainLooper()); 59 | 60 | } 61 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/ReMeasureableLayout.java: -------------------------------------------------------------------------------- 1 | package cellText; 2 | 3 | public interface ReMeasureableLayout { 4 | public void requestLayout(); 5 | public void invalidate(); 6 | public void setPressed(boolean pressed); 7 | public boolean isPressed(); 8 | public void postInvalidate(); 9 | public void postRequestLayout(); 10 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/UiElementFixedCache.java: -------------------------------------------------------------------------------- 1 | package cellText; 2 | 3 | import java.lang.ref.SoftReference; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | 7 | import android.text.TextUtils; 8 | import android.util.SparseArray; 9 | 10 | 11 | //保存UI最常用的图标,不选择softreference为了一直能抓住资源,不被系统释放。 12 | //所以这个cache不能放太多图片,避免占用资源! 13 | 14 | public class UiElementFixedCache { 15 | private static final String TAG = UiElementFixedCache.class.getSimpleName(); 16 | private static UiElementFixedCache mInstance = null; 17 | private static Object lock=new Object(); 18 | 19 | private SparseArray>> mTextCells; 20 | private SoftReference>> mMeasuredTextMapRef; 21 | private UiElementFixedCache() { 22 | mTextCells = new SparseArray>>(); 23 | } 24 | 25 | public static UiElementFixedCache getInstance() { 26 | if (mInstance == null){ 27 | synchronized (lock) { 28 | if (mInstance == null) { 29 | mInstance = new UiElementFixedCache(); 30 | } 31 | } 32 | } 33 | return mInstance; 34 | } 35 | 36 | private HashMap> getHashMap() { 37 | HashMap> ref = 38 | mMeasuredTextMapRef != null ? mMeasuredTextMapRef.get() : null; 39 | if (ref == null) { 40 | ref = new HashMap>(); 41 | mMeasuredTextMapRef = new SoftReference>>(ref); 42 | } 43 | return ref; 44 | } 45 | 46 | public void putMeasuredCells(int measuredWidth, int textSize, String text, MeasuredTextResult measuredTextResult) { 47 | MeasuredTextInfo measuredTextInfo = new MeasuredTextInfo(measuredWidth, textSize, text); 48 | HashMap> map = getHashMap(); 49 | map.put(measuredTextInfo, new SoftReference(measuredTextResult)); 50 | } 51 | 52 | public MeasuredTextResult getMeasuredLines(int measuredWidth, int textSize, String text) { 53 | if (TextUtils.isEmpty(text)) { 54 | return null; 55 | } 56 | 57 | MeasuredTextInfo measuredTextInfo = new MeasuredTextInfo(measuredWidth, textSize, text); 58 | HashMap> map = getHashMap(); 59 | SoftReference measuredLineRef = map.get(measuredTextInfo); 60 | if (measuredLineRef != null) { 61 | MeasuredTextResult measuredTextResult = measuredLineRef.get(); 62 | if (measuredTextResult != null) { 63 | return measuredTextResult.copy(); 64 | } else { 65 | map.remove(measuredTextInfo); 66 | } 67 | } 68 | 69 | return null; 70 | } 71 | 72 | public void putTextCells(int hashcode, ArrayList cells){ 73 | mTextCells.put(hashcode, new SoftReference>(cells)); 74 | } 75 | 76 | public ArrayList getTextCells(int hashcode) { 77 | SoftReference> reference = mTextCells.get(hashcode); 78 | if (reference != null) { 79 | ArrayList cells = reference.get(); 80 | if (cells == null) { 81 | mTextCells.remove(hashcode); 82 | } 83 | return cells; 84 | } 85 | return null; 86 | } 87 | 88 | static class MeasuredTextInfo { 89 | public int measuredWidth; 90 | public int textSize; 91 | public String text; 92 | 93 | public MeasuredTextInfo(int width, int textSize, String text) { 94 | this.measuredWidth = width; 95 | this.textSize = textSize; 96 | this.text = text; 97 | } 98 | 99 | @Override 100 | public int hashCode() { 101 | return measuredWidth + textSize + (text != null?text.hashCode():0); 102 | } 103 | 104 | @Override 105 | public boolean equals(Object obj) { 106 | if (this == obj) { 107 | return true; 108 | } 109 | 110 | if (getClass() == obj.getClass()) { 111 | MeasuredTextInfo o = (MeasuredTextInfo)obj; 112 | return this.measuredWidth == o.measuredWidth 113 | && this.textSize == o.textSize 114 | && this.text.equals(o.text); 115 | } 116 | 117 | return false; 118 | } 119 | } 120 | 121 | public static class MeasuredTextResult { 122 | public ArrayList measuredLines; 123 | public ArrayList lineHeights; 124 | public int measuredWidth; 125 | 126 | @SuppressWarnings("unchecked") 127 | public MeasuredTextResult(ArrayList measuredLines, ArrayList lineHeights, int measuredWidth) { 128 | this.measuredLines = (ArrayList) measuredLines.clone(); 129 | this.lineHeights = (ArrayList) lineHeights.clone(); 130 | this.measuredWidth = measuredWidth; 131 | } 132 | 133 | public MeasuredTextResult copy() { 134 | return new MeasuredTextResult(measuredLines, lineHeights, measuredWidth); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/UrlCell.java: -------------------------------------------------------------------------------- 1 | package cellText; 2 | 3 | import android.content.Intent; 4 | 5 | 6 | public class UrlCell extends ColorTextCell { 7 | 8 | private static final long serialVersionUID = -7091133393080460624L; 9 | 10 | private int action; 11 | 12 | public static final int ACTION_NORMAL = 1; 13 | 14 | public static final int ACTION_GOTODETAIL = ACTION_NORMAL + 1; 15 | 16 | public UrlCell(String txt, String url,String post) { 17 | type = type & ~FLAG_TYPE_MASK | SIGN_URL; 18 | setUrl(url); 19 | text = txt; 20 | this.post = post; 21 | if (url.contains("enterdetail")) { 22 | action = ACTION_GOTODETAIL; 23 | } else { 24 | action = ACTION_NORMAL; 25 | } 26 | // text = CodePointUtils.filter(txt, DefaultCodePointFilter.g()); 27 | } 28 | 29 | @Override 30 | public Intent getIntent() { 31 | Intent intent = new Intent(Intent.ACTION_VIEW); 32 | // intent.setAction(Intent.ACTION_VIEW); 33 | // boolean defaultBroswerEnable = MicroblogApp.getSelf().getSettingManager().getDefaultBroswerEnable(); 34 | // if (defaultBroswerEnable) { 35 | // intent.setData(Uri.parse(Utils.formatInternalBrowserUrlWithParams(url, true, false, false))); 36 | // } else { 37 | // intent.setData(Uri.parse(Utils.formatExternalBrowserUrlWithSid(url))); 38 | // } 39 | return intent; 40 | } 41 | 42 | public int getAction() { 43 | return action; 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/UserNameCell.java: -------------------------------------------------------------------------------- 1 | package cellText; 2 | 3 | 4 | public class UserNameCell extends ColorTextCell { 5 | 6 | private static final long serialVersionUID = 90745655000732280L; 7 | public UserNameCell() { 8 | this(TextCell.SIGN_USER); 9 | } 10 | 11 | public UserNameCell(int type) { 12 | this(type, null); 13 | } 14 | 15 | public UserNameCell(int type, String text) { 16 | super(type, text); 17 | init(type, text); 18 | } 19 | 20 | private void init(int type, String text) { 21 | /* 22 | setTextColor(DLApp.getContext() 23 | .getResources().getColor(R.color.feed_user_name)); 24 | */ 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/touchbehavior/SingleClickBehavior.java: -------------------------------------------------------------------------------- 1 | package cellText.touchbehavior; 2 | 3 | import android.view.MotionEvent; 4 | 5 | 6 | public class SingleClickBehavior extends TouchBehavior { 7 | 8 | private static final int CLICK_AREA = TouchAnalizer.CLICK_AREA; 9 | private float lastPosX = -1, lastPosY = -1; 10 | 11 | public SingleClickBehavior(TouchAnalizer manager) { 12 | super(manager); 13 | behaviorType = TouchAnalizer.BehaviorType.SINGLE_CLICK; 14 | } 15 | 16 | private boolean checkOutside(MotionEvent curr) { 17 | if (curr == null || lastPosX == -1 || lastPosY == -1) { 18 | return true; 19 | } 20 | float dx = lastPosX - curr.getX(); 21 | float dy = lastPosY - curr.getY(); 22 | boolean ret = dx * dx + dy * dy > CLICK_AREA; 23 | return ret; 24 | } 25 | 26 | @Override 27 | public int analizeTouchEvent(MotionEvent event) { 28 | int ret = RET_CONTINUE; 29 | switch (event.getAction()) { 30 | case MotionEvent.ACTION_DOWN: 31 | lastPosX = event.getX(); 32 | lastPosY = event.getY(); 33 | if (judger != null) { 34 | ret = judger.judgeEvent(behaviorType, lastPosX, lastPosY, 0); 35 | } 36 | break; 37 | case MotionEvent.ACTION_MOVE: 38 | if (checkOutside(event)) { 39 | ret = RET_FAILED; 40 | } else { 41 | ret = RET_CONTINUE; 42 | } 43 | break; 44 | case MotionEvent.ACTION_UP: 45 | if (checkOutside(event)) { 46 | ret = RET_FAILED; 47 | } else { 48 | ret = RET_MATCHED; 49 | } 50 | break; 51 | default: 52 | ret = RET_FAILED; 53 | break; 54 | } 55 | if (ret == RET_FAILED || ret == RET_MATCHED) { 56 | lastPosX = lastPosY = -1; 57 | } 58 | return ret; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/touchbehavior/TouchAnalizer.java: -------------------------------------------------------------------------------- 1 | package cellText.touchbehavior; 2 | 3 | 4 | 5 | import android.view.MotionEvent; 6 | 7 | 8 | public class TouchAnalizer { 9 | 10 | public static class BehaviorType { 11 | public static final int SINGLE_CLICK = 0; // 必须按照顺序 12 | public static final int DOUBLE_CLICK = 1; 13 | public static final int DRAG = 2; 14 | public static final int SINGLE_DRAG = 3; // for API level 3- 15 | public static final int SLASH = 4; 16 | public static final int MULTI_SLASH = 5; 17 | public static final int LONG_CLICK = 6; 18 | public static final int ROTATE = 7; 19 | 20 | public static final int MAX_TYPE_COUNT = 8; // 记得修改最大值 21 | }; 22 | 23 | // public static enum BehaviorType { 24 | // SINGLE_CLICK, 25 | // DOUBLE_CLICK, 26 | // DRAG, 27 | // SINGLE_DRAG, // for API level 3- 28 | // SLASH, 29 | // MULTI_SLASH, 30 | // LONG_CLICK, 31 | // //TRIPLE_CLICK, 32 | // PINCH, 33 | // ROTATE, 34 | // }; 35 | 36 | public static int CLICK_AREA = 400; 37 | public static int CLICK_AREA_SEC = 900; 38 | 39 | private TouchBehaviorListener[] listeners = new TouchBehaviorListener[BehaviorType.MAX_TYPE_COUNT]; 40 | private TouchBehavior[] analizers = new TouchBehavior[BehaviorType.MAX_TYPE_COUNT]; 41 | 42 | 43 | public void setListener(int behavior, TouchBehaviorListener l) { 44 | setListener(behavior, l, null); 45 | } 46 | public void setListener(int behaviorType, TouchBehaviorListener l, TouchBehavior.TouchBehaviorEventJudger judger) { 47 | int seq = behaviorType; 48 | listeners[seq] = l; 49 | if (l == null) { 50 | analizers[seq] = null; 51 | } else { 52 | if (analizers[seq] == null) { 53 | analizers[seq] = createTouchBehavior(behaviorType); 54 | } 55 | } 56 | if (analizers[seq] != null && judger != null) { 57 | analizers[seq].setJudger(judger); 58 | } 59 | } 60 | 61 | private TouchBehavior createTouchBehavior(int behavior) { 62 | switch (behavior) { 63 | case BehaviorType.SINGLE_CLICK: 64 | return new SingleClickBehavior(this); 65 | // case DOUBLE_CLICK: 66 | // return new DoubleClickBehavior(this); 67 | // case DRAG: 68 | // return new DragBehavior(this); 69 | // case SINGLE_DRAG: 70 | // return new DragBehaviorSinglePoint(this); 71 | // case SLASH: 72 | // return new SlashBehavior(this); 73 | // case MULTI_SLASH: 74 | // return new MultiSlashBehavior(this); 75 | // case LONG_CLICK: 76 | // return new LongClickBehavior(this); 77 | // case PINCH: 78 | // return new PinchBehavior(this); 79 | // case ROTATE: 80 | // return new RotateBehavior(this); 81 | } 82 | return null; 83 | } 84 | 85 | public boolean inputTouchEvent(MotionEvent event) { 86 | boolean ret = false; 87 | for (TouchBehavior analizer : analizers) { 88 | if (analizer != null) { 89 | if (analizer.onTouchEvent(event)) { 90 | ret = true; 91 | } 92 | } 93 | } 94 | return ret; 95 | } 96 | 97 | public void pauseBehavior(int behaviorType) { 98 | if (analizers[behaviorType] != null) { 99 | analizers[behaviorType].pause(); 100 | } 101 | } 102 | 103 | public boolean onBehavior(int behaviorType, float x, float y) { 104 | return onBehavior(behaviorType, x, y, -1); 105 | } 106 | 107 | public boolean onBehavior(int behaviorType, float x, float y, int state) { 108 | if (listeners[behaviorType] != null) { 109 | return listeners[behaviorType].onInvoke(behaviorType, x, y, state); 110 | } else { 111 | return false; 112 | } 113 | } 114 | 115 | 116 | } 117 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/touchbehavior/TouchBehavior.java: -------------------------------------------------------------------------------- 1 | package cellText.touchbehavior; 2 | 3 | import android.view.MotionEvent; 4 | 5 | 6 | public abstract class TouchBehavior { 7 | 8 | public final static int RET_CONTINUE = 0; 9 | public final static int RET_CONTINUE_FORCE_MORE = 5; 10 | public final static int RET_FAILED = 1; 11 | public final static int RET_MATCHED = 2; 12 | public final static int RET_MATCHED_AND_CALLED_TRUE = 3; 13 | public final static int RET_MATCHED_AND_CALLED_FALSE = 4; 14 | 15 | protected TouchAnalizer manager; 16 | protected int behaviorType; 17 | private boolean paused = false; 18 | protected TouchBehaviorEventJudger judger; 19 | 20 | public TouchBehavior(TouchAnalizer manager) { 21 | this.manager = manager; 22 | } 23 | 24 | public void pause() { 25 | paused = true; 26 | } 27 | 28 | public void setJudger(TouchBehaviorEventJudger judger) { 29 | this.judger = judger; 30 | } 31 | 32 | public boolean onTouchEvent(MotionEvent event) { 33 | if (paused) { 34 | if (event.getAction() == MotionEvent.ACTION_DOWN) { 35 | paused = false; 36 | } else { 37 | return false; 38 | } 39 | } 40 | int ret = analizeTouchEvent(event); 41 | if (ret == RET_CONTINUE) { 42 | return false; 43 | } else if (ret == RET_FAILED) { 44 | paused = true; 45 | return false; 46 | } else if (ret == RET_MATCHED) { 47 | return manager.onBehavior(behaviorType, event.getX(), event.getY()); 48 | } else if (ret == RET_MATCHED_AND_CALLED_TRUE) { 49 | return true; 50 | } else if (ret == RET_MATCHED_AND_CALLED_FALSE) { 51 | return false; 52 | } else if (ret == RET_CONTINUE_FORCE_MORE) { 53 | return true; 54 | } 55 | 56 | // will not come to this case below. 57 | paused = true; 58 | return false; 59 | } 60 | 61 | public abstract int analizeTouchEvent(MotionEvent event); 62 | 63 | public interface TouchBehaviorEventJudger { 64 | public int judgeEvent(int behaviorType, float x, float y, int state); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/cellText/touchbehavior/TouchBehaviorListener.java: -------------------------------------------------------------------------------- 1 | package cellText.touchbehavior; 2 | 3 | 4 | 5 | public interface TouchBehaviorListener { 6 | public boolean onInvoke(int behaviorType, float x, float y, int state); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/android/grafika/gles/FlatShadedProgram.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | import android.opengl.GLES20; 20 | import android.util.Log; 21 | 22 | import java.nio.FloatBuffer; 23 | 24 | /** 25 | * GL program and supporting functions for flat-shaded rendering. 26 | */ 27 | public class FlatShadedProgram { 28 | private static final String TAG = GlUtil.TAG; 29 | 30 | private static final String VERTEX_SHADER = 31 | "uniform mat4 uMVPMatrix;" + 32 | "attribute vec4 aPosition;" + 33 | "void main() {" + 34 | " gl_Position = uMVPMatrix * aPosition;" + 35 | "}"; 36 | 37 | private static final String FRAGMENT_SHADER = 38 | "precision mediump float;" + 39 | "uniform vec4 uColor;" + 40 | "void main() {" + 41 | " gl_FragColor = uColor;" + 42 | "}"; 43 | 44 | // Handles to the GL program and various components of it. 45 | private int mProgramHandle = -1; 46 | private int muColorLoc = -1; 47 | private int muMVPMatrixLoc = -1; 48 | private int maPositionLoc = -1; 49 | 50 | 51 | /** 52 | * Prepares the program in the current EGL context. 53 | */ 54 | public FlatShadedProgram() { 55 | mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER); 56 | if (mProgramHandle == 0) { 57 | throw new RuntimeException("Unable to create program"); 58 | } 59 | Log.d(TAG, "Created program " + mProgramHandle); 60 | 61 | // get locations of attributes and uniforms 62 | 63 | maPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition"); 64 | GlUtil.checkLocation(maPositionLoc, "aPosition"); 65 | muMVPMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix"); 66 | GlUtil.checkLocation(muMVPMatrixLoc, "uMVPMatrix"); 67 | muColorLoc = GLES20.glGetUniformLocation(mProgramHandle, "uColor"); 68 | GlUtil.checkLocation(muColorLoc, "uColor"); 69 | } 70 | 71 | /** 72 | * Releases the program. 73 | */ 74 | public void release() { 75 | GLES20.glDeleteProgram(mProgramHandle); 76 | mProgramHandle = -1; 77 | } 78 | 79 | /** 80 | * Issues the draw call. Does the full setup on every call. 81 | * 82 | * @param mvpMatrix The 4x4 projection matrix. 83 | * @param color A 4-element color vector. 84 | * @param vertexBuffer Buffer with vertex data. 85 | * @param firstVertex Index of first vertex to use in vertexBuffer. 86 | * @param vertexCount Number of vertices in vertexBuffer. 87 | * @param coordsPerVertex The number of coordinates per vertex (e.g. x,y is 2). 88 | * @param vertexStride Width, in bytes, of the data for each vertex (often vertexCount * 89 | * sizeof(float)). 90 | */ 91 | public void draw(float[] mvpMatrix, float[] color, FloatBuffer vertexBuffer, 92 | int firstVertex, int vertexCount, int coordsPerVertex, int vertexStride) { 93 | GlUtil.checkGlError("draw start"); 94 | 95 | // Select the program. 96 | GLES20.glUseProgram(mProgramHandle); 97 | GlUtil.checkGlError("glUseProgram"); 98 | 99 | // Copy the model / view / projection matrix over. 100 | GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mvpMatrix, 0); 101 | GlUtil.checkGlError("glUniformMatrix4fv"); 102 | 103 | // Copy the color vector in. 104 | GLES20.glUniform4fv(muColorLoc, 1, color, 0); 105 | GlUtil.checkGlError("glUniform4fv "); 106 | 107 | // Enable the "aPosition" vertex attribute. 108 | GLES20.glEnableVertexAttribArray(maPositionLoc); 109 | GlUtil.checkGlError("glEnableVertexAttribArray"); 110 | 111 | // Connect vertexBuffer to "aPosition". 112 | GLES20.glVertexAttribPointer(maPositionLoc, coordsPerVertex, 113 | GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); 114 | GlUtil.checkGlError("glVertexAttribPointer"); 115 | 116 | // Draw the rect. 117 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, firstVertex, vertexCount); 118 | GlUtil.checkGlError("glDrawArrays"); 119 | 120 | // Done -- disable vertex array and program. 121 | GLES20.glDisableVertexAttribArray(maPositionLoc); 122 | GLES20.glUseProgram(0); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/android/grafika/gles/FullFrameRect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | import android.opengl.Matrix; 20 | 21 | /** 22 | * This class essentially represents a viewport-sized sprite that will be rendered with 23 | * a texture, usually from an external source like the camera or video decoder. 24 | */ 25 | public class FullFrameRect { 26 | private final Drawable2d mRectDrawable = new Drawable2d(Drawable2d.Prefab.FULL_RECTANGLE); 27 | private Texture2dProgram mProgram; 28 | 29 | /** 30 | * Prepares the object. 31 | * 32 | * @param program The program to use. FullFrameRect takes ownership, and will release 33 | * the program when no longer needed. 34 | */ 35 | public FullFrameRect(Texture2dProgram program) { 36 | mProgram = program; 37 | } 38 | 39 | /** 40 | * Releases resources. 41 | *

42 | * This must be called with the appropriate EGL context current (i.e. the one that was 43 | * current when the constructor was called). If we're about to destroy the EGL context, 44 | * there's no value in having the caller make it current just to do this cleanup, so you 45 | * can pass a flag that will tell this function to skip any EGL-context-specific cleanup. 46 | */ 47 | public void release(boolean doEglCleanup) { 48 | if (mProgram != null) { 49 | if (doEglCleanup) { 50 | mProgram.release(); 51 | } 52 | mProgram = null; 53 | } 54 | } 55 | 56 | /** 57 | * Returns the program currently in use. 58 | */ 59 | public Texture2dProgram getProgram() { 60 | return mProgram; 61 | } 62 | 63 | /** 64 | * Changes the program. The previous program will be released. 65 | *

66 | * The appropriate EGL context must be current. 67 | */ 68 | public void changeProgram(Texture2dProgram program) { 69 | mProgram.release(); 70 | mProgram = program; 71 | } 72 | 73 | /** 74 | * Creates a texture object suitable for use with drawFrame(). 75 | */ 76 | public int createTextureObject() { 77 | return mProgram.createTextureObject(); 78 | } 79 | 80 | /** 81 | * Draws a viewport-filling rect, texturing it with the specified texture object. 82 | */ 83 | public void drawFrame(int textureId, float[] texMatrix) { 84 | // Use the identity matrix for MVP so our 2x2 FULL_RECTANGLE covers the viewport. 85 | mProgram.draw(GlUtil.IDENTITY_MATRIX, mRectDrawable.getVertexArray(), 0, 86 | mRectDrawable.getVertexCount(), mRectDrawable.getCoordsPerVertex(), 87 | mRectDrawable.getVertexStride(), 88 | texMatrix, mRectDrawable.getTexCoordArray(), textureId, 89 | mRectDrawable.getTexCoordStride()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/android/grafika/gles/OffscreenSurface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | /** 20 | * Off-screen EGL surface (pbuffer). 21 | *

22 | * It's good practice to explicitly release() the surface, preferably from a "finally" block. 23 | */ 24 | public class OffscreenSurface extends EglSurfaceBase { 25 | /** 26 | * Creates an off-screen surface with the specified width and height. 27 | */ 28 | public OffscreenSurface(EglCore eglCore, int width, int height) { 29 | super(eglCore); 30 | createOffscreenSurface(width, height); 31 | } 32 | 33 | /** 34 | * Releases any resources associated with the surface. 35 | */ 36 | public void release() { 37 | releaseEglSurface(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/android/grafika/gles/WindowSurface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.grafika.gles; 18 | 19 | import android.graphics.SurfaceTexture; 20 | import android.view.Surface; 21 | 22 | /** 23 | * Recordable EGL window surface. 24 | *

25 | * It's good practice to explicitly release() the surface, preferably from a "finally" block. 26 | */ 27 | public class WindowSurface extends EglSurfaceBase { 28 | private Surface mSurface; 29 | private boolean mReleaseSurface; 30 | 31 | /** 32 | * Associates an EGL surface with the native window surface. 33 | *

34 | * Set releaseSurface to true if you want the Surface to be released when release() is 35 | * called. This is convenient, but can interfere with framework classes that expect to 36 | * manage the Surface themselves (e.g. if you release a SurfaceView's Surface, the 37 | * surfaceDestroyed() callback won't fire). 38 | */ 39 | public WindowSurface(EglCore eglCore, Surface surface, boolean releaseSurface) { 40 | super(eglCore); 41 | createWindowSurface(surface); 42 | mSurface = surface; 43 | mReleaseSurface = releaseSurface; 44 | } 45 | 46 | /** 47 | * Associates an EGL surface with the SurfaceTexture. 48 | */ 49 | public WindowSurface(EglCore eglCore, SurfaceTexture surfaceTexture) { 50 | super(eglCore); 51 | createWindowSurface(surfaceTexture); 52 | } 53 | 54 | /** 55 | * Releases any resources associated with the EGL surface (and, if configured to do so, 56 | * with the Surface as well). 57 | *

58 | * Does not require that the surface's EGL context be current. 59 | */ 60 | public void release() { 61 | releaseEglSurface(); 62 | if (mSurface != null) { 63 | if (mReleaseSurface) { 64 | mSurface.release(); 65 | } 66 | mSurface = null; 67 | } 68 | } 69 | 70 | /** 71 | * Recreate the EGLSurface, using the new EglBase. The caller should have already 72 | * freed the old EGLSurface with releaseEglSurface(). 73 | *

74 | * This is useful when we want to update the EGLSurface associated with a Surface. 75 | * For example, if we want to share with a different EGLContext, which can only 76 | * be done by tearing down and recreating the context. (That's handled by the caller; 77 | * this just creates a new EGLSurface for the Surface we were handed earlier.) 78 | *

79 | * If the previous EGLSurface isn't fully destroyed, e.g. it's still current on a 80 | * context somewhere, the create call will fail with complaints from the Surface 81 | * about already being connected. 82 | */ 83 | public void recreate(EglCore newEglCore) { 84 | if (mSurface == null) { 85 | throw new RuntimeException("not yet implemented for SurfaceTexture"); 86 | } 87 | mEglCore = newEglCore; // switch to new context 88 | createWindowSurface(mSurface); // create new surface 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/App.java: -------------------------------------------------------------------------------- 1 | package com.sample; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.github.anrwatchdog.ANRWatchDog; 7 | import com.sample.hotfix.dex.MultiDex; 8 | import com.sample.performance.ActivityLifeCycleTimeUseTracker; 9 | import com.sample.performance.ViewHericacy; 10 | 11 | public class App extends Application { 12 | 13 | public App() { 14 | super(); 15 | } 16 | 17 | private static Context mAppContext; 18 | 19 | public static Context getContext() { 20 | return mAppContext; 21 | } 22 | 23 | @Override 24 | public void onCreate() { 25 | super.onCreate(); 26 | mAppContext = this; 27 | new ANRWatchDog().start(); 28 | 29 | ActivityLifeCycleTimeUseTracker.getInstance().start(); 30 | ViewHericacy.trackViewTreeDepth(this); 31 | } 32 | 33 | @Override 34 | protected void attachBaseContext(Context base) { 35 | super.attachBaseContext(base); 36 | MultiDex.install(this, "/data/local/tmp/test.dex"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | 6 | import java.io.IOException; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * Created by clarkehe on 1/19/16. 12 | * Todo: 13 | */ 14 | public class BaseActivity extends Activity { 15 | 16 | 17 | @Override 18 | protected void onPause() { 19 | super.onPause(); 20 | 21 | try{ 22 | dumpOverdraw(); 23 | } 24 | catch (Exception e){} 25 | } 26 | 27 | private void dumpOverdraw() throws IOException { 28 | 29 | try { 30 | Class view = Class.forName("android.view.View"); 31 | 32 | Method methodGetHardwareRenderer = view.getDeclaredMethod("getHardwareRenderer"); 33 | methodGetHardwareRenderer.setAccessible(true); 34 | Object Gl20Renderer = methodGetHardwareRenderer.invoke(getWindow().getDecorView()); 35 | 36 | if (Gl20Renderer == null) { 37 | Log.d("过度绘制测试", " 异常,Gl20Renderer为空"); 38 | return; 39 | } 40 | 41 | Field fieldDebugOverdrawLayer = Gl20Renderer.getClass().getSuperclass().getDeclaredField("mDebugOverdrawLayer"); 42 | fieldDebugOverdrawLayer.setAccessible(true); 43 | Object GLES20RenderLayer = fieldDebugOverdrawLayer.get(Gl20Renderer); 44 | 45 | if (GLES20RenderLayer == null) { 46 | Log.d("过度绘制测试", " 异常,GLES20RenderLayer为空"); 47 | return; 48 | } 49 | 50 | Method methodGetCanvas = GLES20RenderLayer.getClass().getSuperclass().getSuperclass().getDeclaredMethod("getCanvas"); 51 | methodGetCanvas.setAccessible(true); 52 | Object GLES20Canvas = methodGetCanvas.invoke(GLES20RenderLayer); 53 | 54 | Method methodGetOverdraw = Gl20Renderer.getClass().getDeclaredMethod("getOverdraw", Class.forName("android.view.HardwareCanvas")); 55 | methodGetOverdraw.setAccessible(true); 56 | Float result = (Float) methodGetOverdraw.invoke(Gl20Renderer, GLES20Canvas); 57 | 58 | String path = getClass().getPackage().getName() + "." + getClass().getSimpleName() + ".java"; 59 | Log.d("过度绘制测试", ""); 60 | 61 | } catch (Exception e) { 62 | Log.d("过度绘制测试", " 异常"); 63 | } 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/Fragment/MyActivtiy.java: -------------------------------------------------------------------------------- 1 | package com.sample.Fragment; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.v4.app.FragmentActivity; 6 | import android.util.AttributeSet; 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.Button; 11 | 12 | import com.sample.R; 13 | 14 | 15 | /** 16 | * Created by clarkehe on 3/22/16. 17 | * Todo: 18 | */ 19 | public class MyActivtiy extends FragmentActivity { 20 | 21 | @Override 22 | public View onCreateView(String name, Context context, AttributeSet attrs) { 23 | return super.onCreateView(name, context, attrs); 24 | } 25 | 26 | @Override 27 | protected void onSaveInstanceState(Bundle outState) { 28 | super.onSaveInstanceState(outState); 29 | Log.d("MyActivtiy", "onSaveInstanceState"); 30 | } 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.layout_fragment); 36 | } 37 | 38 | @Override 39 | protected void onStop() { 40 | super.onStop(); 41 | } 42 | 43 | public MyActivtiy() { 44 | super(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/Fragment/MyFragment.java: -------------------------------------------------------------------------------- 1 | package com.sample.Fragment; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import com.sample.MainActivity; 12 | import com.sample.R; 13 | 14 | /** 15 | * Created by clarkehe on 3/22/16. 16 | * Todo: 17 | */ 18 | public class MyFragment extends Fragment { 19 | @Override 20 | public void onAttach(Activity activity) { 21 | super.onAttach(activity); 22 | } 23 | 24 | @Override 25 | public void onDetach() { 26 | super.onDetach(); 27 | } 28 | 29 | @Override 30 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 31 | //return super.onCreateView(inflater, container, savedInstanceState); 32 | View view = inflater.inflate(R.layout.fragment, container, false); 33 | view.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { 34 | @Override 35 | public void onClick(View v) { 36 | Intent intent = new Intent(MyFragment.this.getActivity(), MainActivity.class); 37 | MyFragment.this.getActivity().startActivity(intent); 38 | } 39 | }); 40 | return view; 41 | } 42 | 43 | @Override 44 | public void onViewCreated(View view, Bundle savedInstanceState) { 45 | super.onViewCreated(view, savedInstanceState); 46 | } 47 | 48 | @Override 49 | public void onStart() { 50 | super.onStart(); 51 | } 52 | 53 | @Override 54 | public void onResume() { 55 | super.onResume(); 56 | } 57 | 58 | @Override 59 | public void onPause() { 60 | super.onPause(); 61 | } 62 | 63 | @Override 64 | public void onStop() { 65 | super.onStop(); 66 | } 67 | 68 | @Override 69 | public void onDestroy() { 70 | super.onDestroy(); 71 | } 72 | 73 | @Override 74 | public void onDestroyView() { 75 | super.onDestroyView(); 76 | } 77 | 78 | public MyFragment() { 79 | super(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/MyLinearLayout.java: -------------------------------------------------------------------------------- 1 | package com.sample; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.LinearLayout; 6 | 7 | /** 8 | * Created by fclarke on 12/23/15. 9 | */ 10 | public class MyLinearLayout extends LinearLayout{ 11 | 12 | public MyLinearLayout(Context context) { 13 | super(context); 14 | } 15 | 16 | public MyLinearLayout(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | @Override 21 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 22 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 23 | } 24 | 25 | @Override 26 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 27 | super.onLayout(changed, l, t, r, b); 28 | } 29 | 30 | public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { 31 | super(context, attrs, defStyleAttr); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/ScreenShotView.java: -------------------------------------------------------------------------------- 1 | package com.sample; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Canvas; 5 | import android.view.View; 6 | 7 | /** 8 | * Created by clarkehe on 1/28/16. 9 | * Todo: 10 | */ 11 | public class ScreenShotView { 12 | 13 | public Bitmap screenShot(View view) { 14 | Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); 15 | Canvas canvas = new Canvas(bitmap); 16 | view.draw(canvas); 17 | return bitmap; 18 | } 19 | 20 | public Bitmap getMagicDrawingCache(View view) { 21 | 22 | Bitmap bitmap = null;//(Bitmap) view.getTag(cacheBitmapKey); 23 | 24 | if (view.getWidth() + view.getHeight() == 0) { 25 | view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); 26 | view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); 27 | } 28 | 29 | int viewWidth = view.getWidth(); 30 | int viewHeight = view.getHeight(); 31 | bitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888); 32 | 33 | bitmap.eraseColor(0xffffff); 34 | Canvas canvas = new Canvas(bitmap); 35 | view.draw(canvas); 36 | 37 | return bitmap; 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/TEST/NewTestView.java: -------------------------------------------------------------------------------- 1 | package com.sample.TEST; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.widget.ImageView; 6 | 7 | import com.sample.R; 8 | 9 | /** 10 | * Created by clarkehe on 1/14/16. 11 | * Todo: 12 | */ 13 | public class NewTestView { 14 | 15 | public NewTestView(Context context) 16 | { 17 | ImageView image = new ImageView(context); 18 | image.setBackgroundResource(R.mipmap.startup_twinkle_animation4); 19 | 20 | TestView view = new TestView(context); 21 | Log.d("TAG", view.toString()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/TEST/TestView.java: -------------------------------------------------------------------------------- 1 | package com.sample.TEST; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import com.sample.R; 7 | 8 | /** 9 | * Created by clarkehe on 1/14/16. 10 | * Todo: TestView 11 | */ 12 | public class TestView { 13 | 14 | public TestView(Context context) 15 | { 16 | ImageView image = new ImageView(context); 17 | image.setBackgroundResource(R.mipmap.startup_twinkle_animation4); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/animator/AnimatorActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.animator; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.view.animation.OvershootInterpolator; 7 | import android.view.animation.TranslateAnimation; 8 | 9 | import com.sample.R; 10 | 11 | public class AnimatorActivity extends Activity { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_animator); 17 | findViewById(R.id.startAnimator).setOnClickListener(new View.OnClickListener() { 18 | @Override 19 | public void onClick(View v) { 20 | start(); 21 | } 22 | }); 23 | } 24 | 25 | void start() { 26 | TranslateAnimation animation = new TranslateAnimation(500, 0, 500, 0); 27 | animation.setDuration(3000); 28 | animation.setInterpolator(new OvershootInterpolator()); 29 | 30 | View view = findViewById(R.id.startAnimator); 31 | view.startAnimation(animation); 32 | 33 | try { 34 | Thread.sleep(3000); 35 | } catch (Exception e) { 36 | 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/anr/ANRTestActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.anr; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.sample.R; 8 | 9 | public class ANRTestActivity extends Activity { 10 | 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_anr_test); 15 | 16 | findViewById(R.id.but).setOnClickListener(new View.OnClickListener() { 17 | @Override 18 | public void onClick(View v) { 19 | try { 20 | Thread.sleep(6000); 21 | } catch (Exception e) { 22 | 23 | } 24 | } 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/architect/mvc/MVCActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.architect.mvc; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.EditText; 8 | import android.widget.TextView; 9 | 10 | import com.sample.R; 11 | 12 | import rx.Subscriber; 13 | import rx.android.schedulers.AndroidSchedulers; 14 | 15 | //MVCActivity相当于MVC中的Control. 16 | 17 | public class MVCActivity extends Activity { 18 | 19 | private EditText cityNOInput; 20 | private TextView city; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_mvc); 26 | initView(); 27 | } 28 | 29 | //初始化View 30 | private void initView() { 31 | cityNOInput = (EditText) findViewById(R.id.zoneId); 32 | city = (TextView) findViewById(R.id.info); 33 | 34 | findViewById(R.id.go).setOnClickListener(new View.OnClickListener() { 35 | @Override 36 | public void onClick(View v) { 37 | //Control call model 38 | getWeather(cityNOInput.getText().toString().trim()); 39 | } 40 | }); 41 | } 42 | 43 | private void getWeather(String cityNumber) { 44 | //Model 45 | new Model().getWeather(cityNumber) 46 | .observeOn(AndroidSchedulers.mainThread()) 47 | .subscribe(new Subscriber() { 48 | @Override 49 | public void onCompleted() { 50 | } 51 | 52 | @Override 53 | public void onError(Throwable e) { 54 | } 55 | 56 | @Override 57 | public void onNext(String result) { 58 | Log.d("TAG", "result:" + result); 59 | //Model notify control 60 | updateView(result); 61 | } 62 | }); 63 | } 64 | 65 | //control update view 66 | private void updateView(final String result) { 67 | city.setText(result); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/architect/mvc/Model.java: -------------------------------------------------------------------------------- 1 | package com.sample.architect.mvc; 2 | 3 | import com.kymjs.rxvolley.RxVolley; 4 | import com.kymjs.rxvolley.rx.Result; 5 | 6 | import rx.Observable; 7 | import rx.functions.Func1; 8 | 9 | public class Model { 10 | 11 | Observable getWeather(final String cityNumber) { 12 | 13 | Observable observable = new RxVolley.Builder() 14 | .url("http://www.weather.com.cn/data/sk/" + cityNumber + ".html") 15 | .contentType(RxVolley.ContentType.JSON) 16 | .getResult(); 17 | 18 | return observable.map(new Func1() { 19 | @Override 20 | public String call(Result result) { 21 | return new String(result.data); 22 | } 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/architect/mvp/model/IModel.java: -------------------------------------------------------------------------------- 1 | package com.sample.architect.mvp.model; 2 | 3 | import rx.Observable; 4 | 5 | public interface IModel { 6 | Observable getWeather(final String cityNumber); 7 | } 8 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/architect/mvp/model/Model.java: -------------------------------------------------------------------------------- 1 | package com.sample.architect.mvp.model; 2 | 3 | import com.kymjs.rxvolley.RxVolley; 4 | import com.kymjs.rxvolley.rx.Result; 5 | 6 | import rx.Observable; 7 | import rx.functions.Func1; 8 | 9 | public class Model implements IModel { 10 | 11 | @Override 12 | public Observable getWeather(final String cityNumber) { 13 | 14 | Observable observable = new RxVolley.Builder() 15 | .url("http://www.weather.com.cn/data/sk/" + cityNumber + ".html") 16 | .contentType(RxVolley.ContentType.JSON) 17 | .getResult(); 18 | 19 | return observable.map(new Func1() { 20 | @Override 21 | public String call(Result result) { 22 | return new String(result.data); 23 | } 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/architect/mvp/presenter/Presenter.java: -------------------------------------------------------------------------------- 1 | package com.sample.architect.mvp.presenter; 2 | 3 | import android.util.Log; 4 | 5 | import com.sample.architect.mvp.model.IModel; 6 | import com.sample.architect.mvp.model.Model; 7 | import com.sample.architect.mvp.view.IView; 8 | 9 | import rx.Subscriber; 10 | import rx.android.schedulers.AndroidSchedulers; 11 | 12 | //P也不直接引用VIEW,而是VIEW的接口。 13 | //定义VIEW的接口也比较麻烦,也可让P直接引用VIEW,这样VM还是可能还是有耦合,但达到了简化了C的目的,有点像VIEW-CONTROL或子的MVC 14 | 15 | public class Presenter { 16 | 17 | private IView mView; 18 | private IModel mMode; 19 | 20 | public Presenter(final IView view) { 21 | mView = view; 22 | mMode = new Model(); 23 | } 24 | 25 | public void updateInfo(final String cityNumber) { 26 | //Model 27 | mMode.getWeather(cityNumber) 28 | .observeOn(AndroidSchedulers.mainThread()) 29 | .subscribe(new Subscriber() { 30 | @Override 31 | public void onCompleted() { 32 | } 33 | 34 | @Override 35 | public void onError(Throwable e) { 36 | } 37 | 38 | @Override 39 | public void onNext(String result) { 40 | Log.d("TAG", "result:" + result); 41 | //update view 42 | mView.updateView(result); 43 | } 44 | }); 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/architect/mvp/view/IView.java: -------------------------------------------------------------------------------- 1 | package com.sample.architect.mvp.view; 2 | 3 | public interface IView { 4 | void updateView(final String info); 5 | } 6 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/architect/mvp/view/MVPActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.architect.mvp.view; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.EditText; 7 | import android.widget.TextView; 8 | 9 | import com.sample.R; 10 | import com.sample.architect.mvp.presenter.Presenter; 11 | 12 | //MVP, 有一部分工作交给了P,MVC中原来的C简单了。 13 | //同时,M与V进一步解耦,V感受不M的存在,是隔离的。 14 | 15 | public class MVPActivity extends Activity implements IView { 16 | 17 | private EditText cityNOInput; 18 | private TextView city; 19 | 20 | private Presenter mPresenter; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_mvp); 26 | initView(); 27 | 28 | //Presenter 29 | mPresenter = new Presenter(this); 30 | } 31 | 32 | //初始化View 33 | private void initView() { 34 | cityNOInput = (EditText) findViewById(R.id.zoneId); 35 | city = (TextView) findViewById(R.id.info); 36 | 37 | findViewById(R.id.go).setOnClickListener(new View.OnClickListener() { 38 | @Override 39 | public void onClick(View v) { 40 | //Use Presenter 41 | getWeather(cityNOInput.getText().toString().trim()); 42 | } 43 | }); 44 | } 45 | 46 | private void getWeather(String cityNumber) { 47 | //Presenter 48 | mPresenter.updateInfo(cityNumber); 49 | } 50 | 51 | @Override 52 | public void updateView(String info) { 53 | city.setText(info); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/attr_style/AttrStyleActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.attr_style; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | import com.sample.R; 7 | 8 | public class AttrStyleActivity extends Activity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_attr_style); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/attr_style/CustomView.java: -------------------------------------------------------------------------------- 1 | package com.sample.attr_style; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.util.AttributeSet; 6 | import android.util.Log; 7 | import android.view.View; 8 | 9 | import com.sample.R; 10 | 11 | /** 12 | * Created by clarkehe on 13/7/16. 13 | */ 14 | public class CustomView extends View { 15 | 16 | private final static String TAG = "CustomView"; 17 | 18 | public CustomView(Context context) { 19 | this(context, null); 20 | } 21 | 22 | public CustomView(Context context, AttributeSet attrs) { 23 | this(context, attrs, R.attr.theme_style); 24 | } 25 | 26 | public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { 27 | super(context, attrs, defStyleAttr); 28 | 29 | final TypedArray a = context.obtainStyledAttributes( 30 | attrs, R.styleable.custom_attrs, defStyleAttr, R.style.default_style); 31 | 32 | String one = a.getString(R.styleable.custom_attrs_custom_attr1); 33 | String two = a.getString(R.styleable.custom_attrs_custom_attr2); 34 | 35 | Log.d(TAG, "one:" + one); 36 | Log.d(TAG, "two:" + two); 37 | 38 | a.recycle(); 39 | } 40 | } 41 | 42 | /** 43 | * 有那些属性: 44 | *

45 | * 属性值的来源: 46 | * 1. AttributeSet, 来自XML指定属性及style 47 | *

48 | * set , 0 , 0 : xm1 xml2 49 | * set, x , 0 : set 没有 theme1, xml2 50 | * set, x, x 51 | */ 52 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/audio/AudioActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.audio; 2 | 3 | import android.os.Bundle; 4 | import android.app.Activity; 5 | import android.os.Handler; 6 | import android.view.View; 7 | 8 | import com.sample.R; 9 | 10 | public class AudioActivity extends Activity { 11 | 12 | PlaySound playSound; 13 | Handler mHandler = new Handler(); 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_audio); 19 | 20 | findViewById(R.id.frontBut).setOnClickListener(new View.OnClickListener() { 21 | @Override 22 | public void onClick(View v) { 23 | play(0, 0); 24 | } 25 | }); 26 | 27 | findViewById(R.id.rightBut).setOnClickListener(new View.OnClickListener() { 28 | @Override 29 | public void onClick(View v) { 30 | play(90, 0); 31 | } 32 | }); 33 | 34 | findViewById(R.id.backBut).setOnClickListener(new View.OnClickListener() { 35 | @Override 36 | public void onClick(View v) { 37 | play(0, 180); 38 | } 39 | }); 40 | } 41 | 42 | float x, y, z; 43 | float t = 0; 44 | 45 | private void play(final float azimuth, final float elevation) { 46 | new Thread(new Runnable() { 47 | @Override 48 | public void run() { 49 | playSound = new PlaySound(AudioActivity.this); 50 | playSound.setPos(azimuth, elevation); 51 | playSound.play(); 52 | } 53 | }).start(); 54 | 55 | postDelay(); 56 | } 57 | 58 | void postDelay() { 59 | mHandler.postDelayed(new Runnable() { 60 | @Override 61 | public void run() { 62 | setPost(); 63 | } 64 | }, 50); 65 | } 66 | 67 | void setPost() { 68 | x = (float) Math.sin(t); 69 | y = (float) Math.cos(t); 70 | z = 0; 71 | t += 0.05; 72 | 73 | float[] ret = IR.cartesianToInteraural(x, y, z); 74 | if (playSound != null) { 75 | playSound.setPos(ret[1], ret[2]); 76 | } 77 | 78 | postDelay(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/audio/Convolve.java: -------------------------------------------------------------------------------- 1 | package com.sample.audio; 2 | 3 | /** 4 | * step 4: HRTF convolution 卷积变换 5 | *

6 | * http://www.digenis.co.uk/?p=81 7 | *

8 | * https://github.com/krruzic/convolver/blob/master/src/convolve.c 9 | */ 10 | 11 | public class Convolve { 12 | 13 | static void Convolve(short[] input, 14 | int inputLength, 15 | float[] filter, 16 | int filterLength, 17 | short[] output) { 18 | // int lengthOfOutput = inputLength + filterLength - 1; 19 | 20 | // for(int i = 0; i < lengthOfOutput; ++i) { 21 | // output[i] = 0; 22 | // } 23 | 24 | for (int i = 0; i < inputLength; ++i) { 25 | for (int j = 0; j < filterLength; ++j) { 26 | output[i + j] += input[i] * filter[j]; 27 | } 28 | } 29 | 30 | /* 31 | int lengthOfOutput = inputLength; 32 | for(int i = 0; i < lengthOfOutput; ++i) 33 | { 34 | output[i] = 0; 35 | 36 | for(int j=0; j getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException { 47 | ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0); 48 | File sourceApk = new File(applicationInfo.sourceDir); 49 | File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME); 50 | 51 | List sourcePaths = new ArrayList(); 52 | sourcePaths.add(applicationInfo.sourceDir); //add the default apk path 53 | 54 | //the prefix of extracted file, ie: test.classes 55 | String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT; 56 | //the total dex numbers 57 | int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1); 58 | 59 | for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) { 60 | //for each dex file, ie: test.classes2.zip, test.classes3.zip... 61 | String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX; 62 | File extractedFile = new File(dexDir, fileName); 63 | if (extractedFile.isFile()) { 64 | sourcePaths.add(extractedFile.getAbsolutePath()); 65 | //we ignore the verify zip part 66 | } else { 67 | throw new IOException("Missing extracted secondary dex file '" + 68 | extractedFile.getPath() + "'"); 69 | } 70 | } 71 | 72 | return sourcePaths; 73 | } 74 | 75 | /** 76 | * get all the classes name in "classes.dex", "classes2.dex", .... 77 | * 78 | * @param context the application context 79 | * @return all the classes name 80 | * @throws PackageManager.NameNotFoundException 81 | * @throws IOException 82 | */ 83 | public static List getAllClasses(Context context) throws PackageManager.NameNotFoundException, IOException { 84 | List classNames = new ArrayList(); 85 | for (String path : getSourcePaths(context)) { 86 | try { 87 | DexFile dexfile = null; 88 | if (path.endsWith(EXTRACTED_SUFFIX)) { 89 | //NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache" 90 | dexfile = DexFile.loadDex(path, path + ".tmp", 0); 91 | } else { 92 | dexfile = new DexFile(path); 93 | } 94 | Enumeration dexEntries = dexfile.entries(); 95 | while (dexEntries.hasMoreElements()) { 96 | classNames.add(dexEntries.nextElement()); 97 | } 98 | } catch (IOException e) { 99 | throw new IOException("Error at loading dex file '" + 100 | path + "'"); 101 | } 102 | } 103 | return classNames; 104 | } 105 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/classLoader/getgameapp/ZipUtil.java: -------------------------------------------------------------------------------- 1 | package com.sample.classLoader.getgameapp; 2 | 3 | /* 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | /* Apache Harmony HEADER because the code in this class comes mostly from ZipFile, ZipEntry and 20 | * ZipConstants from android libcore. 21 | */ 22 | 23 | import java.io.File; 24 | import java.io.IOException; 25 | import java.io.RandomAccessFile; 26 | import java.util.zip.CRC32; 27 | import java.util.zip.ZipException; 28 | 29 | /** 30 | * Tools to build a quick partial crc of zip files. 31 | */ 32 | final class ZipUtil { 33 | static class CentralDirectory { 34 | long offset; 35 | long size; 36 | } 37 | 38 | /* redefine those constant here because of bug 13721174 preventing to compile using the 39 | * constants defined in ZipFile */ 40 | private static final int ENDHDR = 22; 41 | private static final int ENDSIG = 0x6054b50; 42 | 43 | /** 44 | * Size of reading buffers. 45 | */ 46 | private static final int BUFFER_SIZE = 0x4000; 47 | 48 | /** 49 | * Compute crc32 of the central directory of an apk. The central directory contains 50 | * the crc32 of each entries in the zip so the computed result is considered valid for the whole 51 | * zip file. Does not support zip64 nor multidisk but it should be OK for now since ZipFile does 52 | * not either. 53 | */ 54 | static long getZipCrc(File apk) throws IOException { 55 | RandomAccessFile raf = new RandomAccessFile(apk, "r"); 56 | try { 57 | CentralDirectory dir = findCentralDirectory(raf); 58 | 59 | return computeCrcOfCentralDir(raf, dir); 60 | } finally { 61 | raf.close(); 62 | } 63 | } 64 | 65 | /* Package visible for testing */ 66 | static CentralDirectory findCentralDirectory(RandomAccessFile raf) throws IOException, 67 | ZipException { 68 | long scanOffset = raf.length() - ENDHDR; 69 | if (scanOffset < 0) { 70 | throw new ZipException("File too short to be a zip file: " + raf.length()); 71 | } 72 | 73 | long stopOffset = scanOffset - 0x10000 /* ".ZIP file comment"'s max length */; 74 | if (stopOffset < 0) { 75 | stopOffset = 0; 76 | } 77 | 78 | int endSig = Integer.reverseBytes(ENDSIG); 79 | while (true) { 80 | raf.seek(scanOffset); 81 | if (raf.readInt() == endSig) { 82 | break; 83 | } 84 | 85 | scanOffset--; 86 | if (scanOffset < stopOffset) { 87 | throw new ZipException("End Of Central Directory signature not found"); 88 | } 89 | } 90 | 91 | // Read the End Of Central Directory. ENDHDR includes the signature 92 | // bytes, 93 | // which we've already read. 94 | 95 | // Pull out the information we need. 96 | raf.skipBytes(2); // diskNumber 97 | raf.skipBytes(2); // diskWithCentralDir 98 | raf.skipBytes(2); // numEntries 99 | raf.skipBytes(2); // totalNumEntries 100 | CentralDirectory dir = new CentralDirectory(); 101 | dir.size = Integer.reverseBytes(raf.readInt()) & 0xFFFFFFFFL; 102 | dir.offset = Integer.reverseBytes(raf.readInt()) & 0xFFFFFFFFL; 103 | return dir; 104 | } 105 | 106 | /* Package visible for testing */ 107 | static long computeCrcOfCentralDir(RandomAccessFile raf, CentralDirectory dir) 108 | throws IOException { 109 | CRC32 crc = new CRC32(); 110 | long stillToRead = dir.size; 111 | raf.seek(dir.offset); 112 | int length = (int) Math.min(BUFFER_SIZE, stillToRead); 113 | byte[] buffer = new byte[BUFFER_SIZE]; 114 | length = raf.read(buffer, 0, length); 115 | while (length != -1) { 116 | crc.update(buffer, 0, length); 117 | stillToRead -= length; 118 | if (stillToRead == 0) { 119 | break; 120 | } 121 | length = (int) Math.min(BUFFER_SIZE, stillToRead); 122 | length = raf.read(buffer, 0, length); 123 | } 124 | return crc.getValue(); 125 | } 126 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/crashreport/CatchCrash.java: -------------------------------------------------------------------------------- 1 | package com.sample.crashreport; 2 | 3 | import android.util.Log; 4 | 5 | public class CatchCrash { 6 | private final static String TAG = "CatchCrash"; 7 | private static Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler; 8 | 9 | public static void catchUnhandledException() { 10 | defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); 11 | Thread.setDefaultUncaughtExceptionHandler(new CustomUncaughtExceptionHandler()); 12 | } 13 | 14 | private static class CustomUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { 15 | @Override 16 | public void uncaughtException(Thread thread, Throwable ex) { 17 | Log.e(TAG, "***-------------"); 18 | ex.printStackTrace(); 19 | Log.e(TAG, "uncaughtException, thread:" + thread.getName() + ", ex:" + ex.toString()); 20 | Log.e(TAG, "***-------------"); 21 | defaultUncaughtExceptionHandler.uncaughtException(thread, ex); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/crashreport/TestCrashActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.crashreport; 2 | 3 | import android.os.Bundle; 4 | import android.app.Activity; 5 | import android.util.Log; 6 | import android.view.View; 7 | 8 | import com.sample.R; 9 | 10 | public class TestCrashActivity extends Activity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_test_crash); 16 | 17 | findViewById(R.id.catchCrashBut).setOnClickListener(new View.OnClickListener() { 18 | @Override 19 | public void onClick(View v) { 20 | CatchCrash.catchUnhandledException(); 21 | } 22 | }); 23 | 24 | findViewById(R.id.makeCrashBut).setOnClickListener(new View.OnClickListener() { 25 | @Override 26 | public void onClick(View v) { 27 | Thread thread = new Thread(new Runnable() { 28 | @Override 29 | public void run() { 30 | String test = null; 31 | Log.d("test", "" + test.length()); 32 | } 33 | }); 34 | 35 | thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 36 | @Override 37 | public void uncaughtException(Thread thread, Throwable ex) { 38 | Log.d("TEST", "private: uncaughtException"); 39 | } 40 | }); 41 | 42 | thread.start(); 43 | } 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/dialog/CustomDialog.java: -------------------------------------------------------------------------------- 1 | package com.sample.dialog; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | 7 | import com.sample.R; 8 | 9 | public class CustomDialog extends Dialog { 10 | 11 | public CustomDialog(Context context) { 12 | super(context); 13 | } 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.custom_dialog); 19 | this.setTitle("Title"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/dialog/DialogActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.dialog; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.sample.R; 8 | 9 | public class DialogActivity extends Activity { 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_dialog); 14 | 15 | findViewById(R.id.butShowDialog).setOnClickListener(new View.OnClickListener() { 16 | @Override 17 | public void onClick(View v) { 18 | showNormalDialog(); 19 | } 20 | }); 21 | } 22 | 23 | private void showNormalDialog() { 24 | CustomDialog dialog = new CustomDialog(this); 25 | dialog.show(); 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/glide/CustomCachingGlideModule.java: -------------------------------------------------------------------------------- 1 | package com.sample.glide; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.util.Log; 6 | 7 | import com.bumptech.glide.Glide; 8 | import com.bumptech.glide.GlideBuilder; 9 | import com.bumptech.glide.load.data.DataFetcher; 10 | import com.bumptech.glide.load.engine.cache.MemorySizeCalculator; 11 | import com.bumptech.glide.load.model.GenericLoaderFactory; 12 | import com.bumptech.glide.load.model.ModelLoader; 13 | import com.bumptech.glide.load.model.ModelLoaderFactory; 14 | import com.bumptech.glide.load.model.StringLoader; 15 | import com.bumptech.glide.load.model.stream.StreamModelLoader; 16 | import com.bumptech.glide.module.GlideModule; 17 | 18 | import java.io.InputStream; 19 | 20 | public class CustomCachingGlideModule implements GlideModule { 21 | 22 | private final static String TAG = "GlideModule|Glide"; 23 | 24 | @Override 25 | public void applyOptions(Context context, GlideBuilder builder) { 26 | MemorySizeCalculator calculator = new MemorySizeCalculator(context); 27 | int defaultMemoryCacheSize = calculator.getMemoryCacheSize(); 28 | int defaultBitmapPoolSize = calculator.getBitmapPoolSize(); 29 | 30 | Log.d(TAG, "defaultMemoryCacheSize:" + defaultMemoryCacheSize); 31 | Log.d(TAG, "defaultBitmapPoolSize:" + defaultBitmapPoolSize); 32 | 33 | //int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize); 34 | //int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize); 35 | 36 | //defaultMemoryCacheSize = 0; 37 | builder.setMemoryCache(new CustomLruResourceCache(defaultMemoryCacheSize)); 38 | builder.setBitmapPool(new CustomLruBitmapPool(defaultBitmapPoolSize)); 39 | } 40 | 41 | @Override 42 | public void registerComponents(Context context, Glide glide) { 43 | glide.register(String.class, InputStream.class, new CustomImageSizeModelFactory()); 44 | } 45 | 46 | private class CustomImageSizeUrlLoader extends StringLoader implements StreamModelLoader { 47 | @Override 48 | public DataFetcher getResourceFetcher(String model, int width, int height) { 49 | Log.d(TAG, "mode:" + model); 50 | Log.d(TAG, "width:" + width + ",height:" + height); 51 | return super.getResourceFetcher(model, width, height); 52 | } 53 | 54 | public CustomImageSizeUrlLoader(ModelLoader uriLoader) { 55 | super(uriLoader); 56 | } 57 | } 58 | 59 | private class CustomImageSizeModelFactory implements ModelLoaderFactory { 60 | @Override 61 | public ModelLoader build(Context context, GenericLoaderFactory factories) { 62 | return new CustomImageSizeUrlLoader(factories.buildModelLoader(Uri.class, InputStream.class)); 63 | } 64 | 65 | @Override 66 | public void teardown() { 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/glide/CustomLruBitmapPool.java: -------------------------------------------------------------------------------- 1 | package com.sample.glide; 2 | 3 | import android.graphics.Bitmap; 4 | import android.util.Log; 5 | 6 | import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool; 7 | 8 | public class CustomLruBitmapPool extends LruBitmapPool { 9 | 10 | private final static String TAG = "BitmapPool|Glide"; 11 | 12 | private volatile int hitCount; 13 | private volatile int missCount; 14 | 15 | public CustomLruBitmapPool(int maxSize) { 16 | super(maxSize); 17 | } 18 | 19 | @Override 20 | public synchronized Bitmap get(int width, int height, Bitmap.Config config) { 21 | Log.d(TAG, "get, width:" + width + ",height:" + height); 22 | final Bitmap bmp = super.get(width, height, config); 23 | if (bmp != null) { 24 | hitCount += 1; 25 | } else { 26 | missCount += 1; 27 | } 28 | Log.d(TAG, "get:missCount:" + missCount + ",hitCount:" + hitCount); 29 | return bmp; 30 | } 31 | 32 | @Override 33 | public synchronized boolean put(Bitmap bitmap) { 34 | final int width = bitmap.getWidth(); 35 | final int height = bitmap.getHeight(); 36 | Log.d(TAG, "put, width:" + width + ",height:" + height); 37 | return super.put(bitmap); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/glide/CustomLruResourceCache.java: -------------------------------------------------------------------------------- 1 | package com.sample.glide; 2 | 3 | import android.util.Log; 4 | 5 | import com.bumptech.glide.load.Key; 6 | import com.bumptech.glide.load.engine.Resource; 7 | import com.bumptech.glide.load.engine.cache.LruResourceCache; 8 | 9 | public class CustomLruResourceCache extends LruResourceCache { 10 | 11 | private final static String TAG = "ResourceCache|Glide"; 12 | 13 | private volatile int hitCount; 14 | private volatile int missCount; 15 | 16 | public CustomLruResourceCache(int size) { 17 | super(size); 18 | } 19 | 20 | @Override 21 | public Resource put(Key key, Resource item) { 22 | Log.d(TAG, "put"); 23 | return super.put(key, item); 24 | } 25 | 26 | @Override 27 | public Resource get(Key key) { 28 | Log.d(TAG, "get"); 29 | return super.get(key); 30 | } 31 | 32 | @Override 33 | public boolean contains(Key key) { 34 | Log.d(TAG, "contains"); 35 | return super.contains(key); 36 | } 37 | 38 | @Override 39 | public Resource remove(Key key) { 40 | final Resource item = super.remove(key); 41 | if (item == null) { 42 | missCount += 1; 43 | } else { 44 | hitCount += 1; 45 | } 46 | Log.d(TAG, "remove:missCount:" + missCount + ",hitCount:" + hitCount); 47 | return item; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/glide/GlideActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.glide; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.graphics.drawable.Drawable; 7 | import android.os.Bundle; 8 | import android.os.Handler; 9 | import android.util.Log; 10 | import android.view.View; 11 | import android.widget.ImageView; 12 | import android.widget.TextView; 13 | 14 | import com.bumptech.glide.Glide; 15 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 16 | import com.bumptech.glide.request.animation.GlideAnimation; 17 | import com.bumptech.glide.request.target.SimpleTarget; 18 | import com.sample.R; 19 | 20 | // 源图片大小, 21 | // TARGET大小,确定TARGET大小 22 | // 缩放 23 | 24 | // ImageView又有转换(fitcenter, centercrop) 25 | // DECODE 26 | 27 | public class GlideActivity extends Activity { 28 | 29 | private final static String TAG = "GlideActivity|Glide"; 30 | 31 | //980x500 32 | final String url = "http://ossweb-img.qq.com/images/lol/web201310/skin/big222000.jpg"; 33 | 34 | //100x100 35 | final String url2 = "http://q3.qlogo.cn/g?b=qq&k=tysUcdic0LwrPOIK3PeWic3w&s=100&t=1457753232"; 36 | 37 | final String url3 = "http://p.qpic.cn/qqtalk_snapshot/2123176/1468914847796807/320"; 38 | 39 | final String url4 = "http://p.qpic.cn/qtlol/0/b9aa30750469afc192102c090effc820T1468585088986699/420"; 40 | 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_glide); 45 | bindView(); 46 | } 47 | 48 | private void bindView() { 49 | float dimen_10 = getResources().getDimension(R.dimen.dimen_10); 50 | Log.d(TAG, "dimen_10:" + dimen_10); 51 | 52 | final ImageView imageView = (ImageView) findViewById(R.id.imageView); 53 | Glide.with(this).load(url).transform(new GlideCircleTransform(this)).diskCacheStrategy(DiskCacheStrategy.SOURCE) 54 | .into(imageView); 55 | 56 | findViewById(R.id.but).setOnClickListener(new View.OnClickListener() { 57 | @Override 58 | public void onClick(View v) { 59 | final ImageView imageView2 = (ImageView) findViewById(R.id.imageView2); 60 | Glide.with(GlideActivity.this).load(url).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView2); 61 | } 62 | }); 63 | 64 | // new Handler().postDelayed(new Runnable() { 65 | // @Override 66 | // public void run() { 67 | // Drawable drawable = imageView.getDrawable(); 68 | // if (drawable != null) { 69 | // final int width1 = drawable.getIntrinsicWidth(); 70 | // final int height1 = drawable.getIntrinsicHeight(); 71 | // Log.d(TAG, "width1:" + width1); 72 | // Log.d(TAG, "height1:" + height1); 73 | // } 74 | // } 75 | // }, 3000); 76 | 77 | //loadImage(this, url, null, -1, -1); 78 | } 79 | 80 | public static void loadImage(Context context, String url, final TextView tv, final int w, final int h) { 81 | try { 82 | Glide.with(context).load(url).asBitmap().dontAnimate().override(100, 200) 83 | .placeholder(R.drawable.ic_launcher).into(new SimpleTarget() { 84 | @Override 85 | public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { 86 | 87 | final int width = resource.getWidth(); 88 | final int height = resource.getHeight(); 89 | Log.d(TAG, "width:" + width); 90 | Log.d(TAG, "height:" + height); 91 | 92 | if (w > 0 && h > 0) { 93 | 94 | // resource = scaleBitmap(resource, w, h);//Glide的override方法仅适用于ImageView 95 | } 96 | // TabWidgetHelper.setViewIcon(ApplicationContextHolder.getAppContext(), tv, resource, TabWidgetHelper.DrawableOrientation.TOP, false); 97 | } 98 | }); 99 | } catch (IllegalArgumentException | IllegalStateException e) { 100 | e.printStackTrace(); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/glide/GlideCircleTransform.java: -------------------------------------------------------------------------------- 1 | package com.sample.glide; 2 | 3 | 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapShader; 7 | import android.graphics.Canvas; 8 | import android.graphics.Paint; 9 | 10 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 11 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; 12 | 13 | //内存缓存,是缓存转换后的 14 | //磁盘缓存:SOURCE RESULT 15 | 16 | public class GlideCircleTransform extends BitmapTransformation { 17 | 18 | public GlideCircleTransform(Context context) { 19 | super(context); 20 | } 21 | 22 | @Override 23 | protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { 24 | return circleCrop(pool, toTransform); 25 | } 26 | 27 | private static Bitmap circleCrop(BitmapPool pool, Bitmap source) { 28 | if (source == null) return null; 29 | 30 | int size = Math.min(source.getWidth(), source.getHeight()); 31 | int x = (source.getWidth() - size) / 2; 32 | int y = (source.getHeight() - size) / 2; 33 | 34 | final Bitmap squared = Bitmap.createBitmap(source, x, y, size, size); 35 | 36 | Bitmap result = pool.get(size, size, Bitmap.Config.RGB_565); 37 | if (result == null) { 38 | result = Bitmap.createBitmap(size, size, Bitmap.Config.RGB_565); 39 | } 40 | 41 | Canvas canvas = new Canvas(result); 42 | Paint paint = new Paint(); 43 | paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); 44 | paint.setAntiAlias(true); 45 | float r = size / 2f; 46 | canvas.drawCircle(r, r, r, paint); 47 | return result; 48 | } 49 | 50 | @Override 51 | public String getId() { 52 | return getClass().getName(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/hotfix/dex/BugClass.java: -------------------------------------------------------------------------------- 1 | package com.sample.hotfix.dex; 2 | 3 | public class BugClass { 4 | 5 | String getStr() { 6 | return "Bug Class"; 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/hotfix/dex/HotFixActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.hotfix.dex; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.widget.TextView; 7 | 8 | import com.sample.R; 9 | 10 | /** 11 | * 在虚拟机启动的时候,当verify选项被打开的时候,如果static方法、private方法、构造函数等, 12 | * 其中的直接引用(第一层关系)到的类都在同一个dex文件中,那么该类就会被打上CLASS_ISPREVERIFIED标志。 13 | *

14 | * 打上这个标识的作用:跟类优化有关??? 15 | */ 16 | public class HotFixActivity extends Activity { 17 | 18 | private final static String TAG = "HotFixActivity"; 19 | 20 | /** 21 | * 08-15 16:16:36.269 4276-4276/? W/dalvikvm: Class resolved by unexpected DEX: Lcom/sample/hotfix/dex/HotFixActivity;(0x42691000):0x751cc000 ref [Lcom/sample/hotfix/dex/BugClass;] Lcom/sample/hotfix/dex/BugClass;(0x42691000):0x7505c000 22 | * 08-15 16:16:36.269 4276-4276/? W/dalvikvm: (Lcom/sample/hotfix/dex/HotFixActivity; had used a different Lcom/sample/hotfix/dex/BugClass; during pre-verification) 23 | *

24 | * 08-15 16:16:36.269 4276-4276/? E/AndroidRuntime: FATAL EXCEPTION: main 25 | * Process: com.sample, PID: 4276 26 | * java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation 27 | * at com.sample.hotfix.dex.HotFixActivity.onCreate(HotFixActivity.java:20) 28 | * at android.app.Activity.performCreate(Activity.java:5231) 29 | *

30 | * 希望BugClass这个类与HotFixActivity,应该是在一个DEX文件, 31 | */ 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | printClassLoad(); 36 | setContentView(R.layout.activity_hot_fix); 37 | TextView view = (TextView) findViewById(R.id.textView); 38 | view.setText(new BugClass().getStr()); 39 | } 40 | 41 | 42 | /** 43 | * 08-15 16:16:36.259 4276-4276/? I/HotFixActivity: [onCreate] classLoader 1 : dalvik.system.PathClassLoader[DexPathList[[dex file "dalvik.system.DexFile@426939b0", zip file "/data/app/com.sample-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.sample-1, /vendor/lib, /system/lib]]] 44 | * 08-15 16:16:36.259 4276-4276/? I/HotFixActivity: [onCreate] classLoader 1 : java.lang.BootClassLoader@41636fd0 45 | */ 46 | 47 | void printClassLoad() { 48 | ClassLoader classLoader = getClassLoader(); 49 | int i = 1; 50 | if (classLoader != null) { 51 | Log.i(TAG, "[onCreate] classLoader " + i + " : " + classLoader.toString()); 52 | 53 | while (classLoader.getParent() != null) { 54 | classLoader = classLoader.getParent(); 55 | Log.i(TAG, "[onCreate] classLoader " + i + " : " + classLoader.toString()); 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/lifecycle/ActivityA.java: -------------------------------------------------------------------------------- 1 | package com.sample.lifecycle; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.Button; 8 | 9 | public class ActivityA extends Activity{ 10 | 11 | public ActivityA() { 12 | super(); 13 | } 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | Button button = new Button(this); 19 | button.setText("Start Activity B"); 20 | this.setContentView(button); 21 | 22 | button.setOnClickListener(new View.OnClickListener() { 23 | @Override 24 | public void onClick(View v) { 25 | Intent intent = new Intent(ActivityA.this, ActivityB.class); 26 | ActivityA.this.startActivity(intent); 27 | } 28 | }); 29 | } 30 | 31 | static { 32 | RegActivityLifecycleCallback.regLifeCycleCallBack(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/lifecycle/ActivityB.java: -------------------------------------------------------------------------------- 1 | package com.sample.lifecycle; 2 | 3 | import android.app.Activity; 4 | 5 | public class ActivityB extends Activity{ 6 | 7 | public ActivityB() { 8 | super(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/lifecycle/RegActivityLifecycleCallback.java: -------------------------------------------------------------------------------- 1 | package com.sample.lifecycle; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | 8 | import com.sample.App; 9 | 10 | public class RegActivityLifecycleCallback { 11 | 12 | private final static String TAG = "RegActivityLifeCallback"; 13 | 14 | static public void regLifeCycleCallBack() 15 | { 16 | Log.d(TAG, "regLifeCycleCallBack"); 17 | 18 | final Application app = (Application) App.getContext(); 19 | app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { 20 | @Override 21 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 22 | Log.d(TAG, "onActivityCreated, " + activity); 23 | } 24 | 25 | @Override 26 | public void onActivityStarted(Activity activity) { 27 | Log.d(TAG, "onActivityStarted, " + activity); 28 | } 29 | 30 | @Override 31 | public void onActivityResumed(Activity activity) { 32 | Log.d(TAG, "onActivityResumed, " + activity); 33 | } 34 | 35 | @Override 36 | public void onActivityPaused(Activity activity) { 37 | Log.d(TAG, "onActivityPaused, " + activity); 38 | } 39 | 40 | @Override 41 | public void onActivityStopped(Activity activity) { 42 | Log.d(TAG, "onActivityStopped, " + activity); 43 | } 44 | 45 | @Override 46 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 47 | Log.d(TAG, "onActivitySaveInstanceState, " + activity); 48 | } 49 | 50 | @Override 51 | public void onActivityDestroyed(Activity activity) { 52 | Log.d(TAG, "onActivityDestroyed, " + activity); 53 | } 54 | }); 55 | } 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/multiprocess/ProcessActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.multiprocess; 2 | 3 | import com.sample.R; 4 | 5 | import android.app.Activity; 6 | import android.content.ComponentName; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.content.ServiceConnection; 10 | import android.os.Bundle; 11 | import android.os.IBinder; 12 | import android.util.Log; 13 | import android.view.View; 14 | 15 | public class ProcessActivity extends Activity { 16 | 17 | private static String TAG = "ProcessActivity"; 18 | 19 | boolean mBound = false; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_process); 25 | 26 | findViewById(R.id.butStart).setOnClickListener(new View.OnClickListener() { 27 | @Override 28 | public void onClick(View v) { 29 | binderToService(); 30 | } 31 | }); 32 | } 33 | 34 | @Override 35 | protected void onResume() { 36 | super.onResume(); 37 | } 38 | 39 | private void binderToService() { 40 | Intent intent = new Intent(ProcessActivity.this.getApplicationContext(), ProcessService.class); 41 | //startService(intent); 42 | boolean ret = this.getApplicationContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 43 | Log.d(TAG, "binderToService,ret:" + ret); 44 | } 45 | 46 | /** 47 | * Defines callbacks for service binding, passed to bindService() 48 | */ 49 | private ServiceConnection mConnection = new ServiceConnection() { 50 | 51 | @Override 52 | public void onServiceConnected(ComponentName className, 53 | IBinder service) { 54 | // We've bound to LocalService, cast the IBinder and get LocalService instance 55 | //LocalBinder binder = (LocalBinder) service; 56 | //mService = binder.getService(); 57 | Log.d(TAG, "onServiceConnected"); 58 | mBound = true; 59 | } 60 | 61 | @Override 62 | public void onServiceDisconnected(ComponentName arg0) { 63 | Log.d(TAG, "onServiceDisconnected"); 64 | mBound = false; 65 | } 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/multiprocess/ProcessService.java: -------------------------------------------------------------------------------- 1 | package com.sample.multiprocess; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | import android.util.Log; 7 | 8 | public class ProcessService extends Service { 9 | 10 | private final String TAG = "ProcessService"; 11 | 12 | @Override 13 | public IBinder onBind(Intent intent) { 14 | Log.d(TAG, "onBind"); 15 | return null; 16 | } 17 | 18 | @Override 19 | public int onStartCommand(Intent intent, int flags, int startId) { 20 | return super.onStartCommand(intent, flags, startId); 21 | } 22 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/myTextView.java: -------------------------------------------------------------------------------- 1 | package com.sample; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Paint; 6 | import android.graphics.Rect; 7 | import android.util.AttributeSet; 8 | import android.util.Log; 9 | import android.widget.TextView; 10 | 11 | 12 | /** 13 | * Created by clarkehe on 1/15/16. 14 | * Todo: 15 | */ 16 | public class myTextView extends TextView { 17 | 18 | private static final String TAG = "myTextView"; 19 | 20 | public myTextView(Context context) { 21 | super(context); 22 | } 23 | 24 | public myTextView(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | } 27 | 28 | public myTextView(Context context, AttributeSet attrs, int defStyle) { 29 | super(context, attrs, defStyle); 30 | } 31 | 32 | @Override 33 | protected void onDraw(Canvas canvas) { 34 | super.onDraw(canvas); 35 | 36 | Paint.FontMetrics fontMetrics = this.getPaint().getFontMetrics(); 37 | Log.d(TAG, "ascent: " + fontMetrics.ascent); 38 | Log.d(TAG, "descent: " + fontMetrics.descent); 39 | Log.d(TAG, "top: " + fontMetrics.top); 40 | Log.d(TAG, "bottom: " + fontMetrics.bottom); 41 | Log.d(TAG, "leading: " + fontMetrics.leading); 42 | 43 | 44 | String str = this.getText().toString(); 45 | //str = "一"; 46 | Rect rect = new Rect(); 47 | this.getPaint().getTextBounds(str, 0, str.length(), rect); 48 | Log.d(TAG, rect.toString()); 49 | } 50 | 51 | @Override 52 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 53 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 54 | 55 | final int width = getMeasuredWidth(); 56 | final int height = getMeasuredHeight(); 57 | 58 | Log.d(TAG, "width: " + width); 59 | Log.d(TAG, "height: " + height); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/network/NetworkActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.network; 2 | 3 | import android.app.Activity; 4 | import android.os.AsyncTask; 5 | import android.os.Bundle; 6 | 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.widget.TextView; 10 | 11 | import com.facebook.network.connectionclass.*; 12 | import com.sample.R; 13 | 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.net.URL; 17 | import java.net.URLConnection; 18 | 19 | public class NetworkActivity extends Activity { 20 | 21 | private static final String TAG = "ConnectionClass-Sample"; 22 | 23 | private ConnectionClassManager mConnectionClassManager; 24 | private DeviceBandwidthSampler mDeviceBandwidthSampler; 25 | private ConnectionChangedListener mListener; 26 | private TextView mTextView; 27 | private View mRunningBar; 28 | 29 | private String mURL = "http://connectionclass.parseapp.com/m100_hubble_4060.jpg"; 30 | private int mTries = 0; 31 | private ConnectionQuality mConnectionClass = ConnectionQuality.UNKNOWN; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_network); 37 | 38 | mConnectionClassManager = ConnectionClassManager.getInstance(); 39 | mDeviceBandwidthSampler = DeviceBandwidthSampler.getInstance(); 40 | 41 | findViewById(R.id.test_btn).setOnClickListener(testButtonClicked); 42 | mTextView = (TextView) findViewById(R.id.connection_class); 43 | mTextView.setText(mConnectionClassManager.getCurrentBandwidthQuality().toString()); 44 | 45 | mRunningBar = findViewById(R.id.runningBar); 46 | mRunningBar.setVisibility(View.GONE); 47 | 48 | mListener = new ConnectionChangedListener(); 49 | } 50 | 51 | @Override 52 | protected void onPause() { 53 | super.onPause(); 54 | mConnectionClassManager.remove(mListener); 55 | } 56 | 57 | @Override 58 | protected void onResume() { 59 | super.onResume(); 60 | mConnectionClassManager.register(mListener); 61 | } 62 | 63 | /** 64 | * Listener to update the UI upon connectionclass change. 65 | */ 66 | private class ConnectionChangedListener 67 | implements ConnectionClassManager.ConnectionClassStateChangeListener { 68 | 69 | @Override 70 | public void onBandwidthStateChange(ConnectionQuality bandwidthState) { 71 | mConnectionClass = bandwidthState; 72 | runOnUiThread(new Runnable() { 73 | @Override 74 | public void run() { 75 | mTextView.setText(mConnectionClass.toString()); 76 | } 77 | }); 78 | } 79 | } 80 | 81 | private final View.OnClickListener testButtonClicked = new View.OnClickListener() { 82 | @Override 83 | public void onClick(View v) { 84 | new DownloadImage().execute(mURL); 85 | } 86 | }; 87 | 88 | /** 89 | * AsyncTask for handling downloading and making calls to the timer. 90 | */ 91 | private class DownloadImage extends AsyncTask { 92 | 93 | @Override 94 | protected void onPreExecute() { 95 | mDeviceBandwidthSampler.startSampling(); 96 | mRunningBar.setVisibility(View.VISIBLE); 97 | } 98 | 99 | @Override 100 | protected Void doInBackground(String... url) { 101 | String imageURL = url[0]; 102 | try { 103 | // Open a stream to download the image from our URL. 104 | URLConnection connection = new URL(imageURL).openConnection(); 105 | connection.setUseCaches(false); 106 | connection.connect(); 107 | InputStream input = connection.getInputStream(); 108 | try { 109 | byte[] buffer = new byte[1024]; 110 | 111 | // Do some busy waiting while the stream is open. 112 | while (input.read(buffer) != -1) { 113 | } 114 | } finally { 115 | input.close(); 116 | } 117 | } catch (IOException e) { 118 | Log.e(TAG, "Error while downloading image."); 119 | } 120 | return null; 121 | } 122 | 123 | @Override 124 | protected void onPostExecute(Void v) { 125 | mDeviceBandwidthSampler.stopSampling(); 126 | // Retry for up to 10 times until we find a ConnectionClass. 127 | if (mConnectionClass == ConnectionQuality.UNKNOWN && mTries < 10) { 128 | mTries++; 129 | new DownloadImage().execute(mURL); 130 | } 131 | if (!mDeviceBandwidthSampler.isSampling()) { 132 | mRunningBar.setVisibility(View.GONE); 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/opengles/GLSurf.java: -------------------------------------------------------------------------------- 1 | package com.sample.opengles; 2 | 3 | import android.content.Context; 4 | import android.opengl.GLSurfaceView; 5 | 6 | public class GLSurf extends GLSurfaceView { 7 | 8 | private final GLRenderer mRenderer; 9 | 10 | public GLSurf(Context context) { 11 | super(context); 12 | 13 | // Create an OpenGL ES 2.0 context. 14 | setEGLContextClientVersion(2); 15 | 16 | // Set the Renderer for drawing on the GLSurfaceView 17 | mRenderer = new GLRenderer(context); 18 | setRenderer(mRenderer); 19 | 20 | // Render the view only when there is a change in the drawing data 21 | setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); 22 | } 23 | 24 | @Override 25 | public void onPause() { 26 | super.onPause(); 27 | mRenderer.onPause(); 28 | } 29 | 30 | @Override 31 | public void onResume() { 32 | super.onResume(); 33 | mRenderer.onResume(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/opengles/OpenGLActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.opengles; 2 | 3 | import android.opengl.GLSurfaceView; 4 | import android.os.Bundle; 5 | import android.app.Activity; 6 | import android.view.Window; 7 | import android.view.WindowManager; 8 | import android.widget.RelativeLayout; 9 | 10 | import com.sample.R; 11 | 12 | public class OpenGLActivity extends Activity { 13 | 14 | // Our OpenGL Surfaceview 15 | private GLSurfaceView glSurfaceView; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | 20 | // Turn off the window's title bar 21 | requestWindowFeature(Window.FEATURE_NO_TITLE); 22 | 23 | // Super 24 | super.onCreate(savedInstanceState); 25 | 26 | // Fullscreen mode 27 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 28 | 29 | // We create our Surfaceview for our OpenGL here. 30 | glSurfaceView = new GLSurf(this); 31 | 32 | // Set our view. 33 | setContentView(R.layout.activity_opengl); 34 | 35 | // Retrieve our Relative layout from our main layout we just set to our view. 36 | RelativeLayout layout = (RelativeLayout) findViewById(R.id.gamelayout); 37 | 38 | // Attach our surfaceview to our relative layout from our main layout. 39 | RelativeLayout.LayoutParams glParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); 40 | layout.addView(glSurfaceView, glParams); 41 | } 42 | 43 | @Override 44 | protected void onPause() { 45 | super.onPause(); 46 | glSurfaceView.onPause(); 47 | } 48 | 49 | @Override 50 | protected void onResume() { 51 | super.onResume(); 52 | glSurfaceView.onResume(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/opengles/riGraphicTools.java: -------------------------------------------------------------------------------- 1 | package com.sample.opengles; 2 | 3 | import android.opengl.GLES20; 4 | 5 | public class riGraphicTools { 6 | 7 | // Program variables 8 | public static int sp_SolidColor; 9 | public static int sp_Image; 10 | 11 | 12 | /* SHADER Solid 13 | * 14 | * This shader is for rendering a colored primitive. 15 | * 16 | */ 17 | public static final String vs_SolidColor = 18 | "uniform mat4 uMVPMatrix;" + 19 | "attribute vec4 vPosition;" + 20 | "void main() {" + 21 | " gl_Position = uMVPMatrix * vPosition;" + 22 | "}"; 23 | 24 | public static final String fs_SolidColor = 25 | "precision mediump float;" + 26 | "void main() {" + 27 | " gl_FragColor = vec4(0.5,0,0,1);" + 28 | "}"; 29 | 30 | /* SHADER Image 31 | * 32 | * This shader is for rendering 2D images straight from a texture 33 | * No additional effects. 34 | * 35 | */ 36 | public static final String vs_Image = 37 | "uniform mat4 uMVPMatrix;" + 38 | "attribute vec4 vPosition;" + 39 | "attribute vec2 a_texCoord;" + 40 | "varying vec2 v_texCoord;" + 41 | "void main() {" + 42 | " gl_Position = uMVPMatrix * vPosition;" + 43 | " v_texCoord = a_texCoord;" + 44 | "}"; 45 | 46 | public static final String fs_Image = 47 | "precision mediump float;" + 48 | "varying vec2 v_texCoord;" + 49 | "uniform sampler2D s_texture;" + 50 | "void main() {" + 51 | " gl_FragColor = texture2D( s_texture, v_texCoord );" + 52 | "}"; 53 | 54 | 55 | public static int loadShader(int type, String shaderCode) { 56 | 57 | // create a vertex shader type (GLES20.GL_VERTEX_SHADER) 58 | // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) 59 | int shader = GLES20.glCreateShader(type); 60 | 61 | // add the source code to the shader and compile it 62 | GLES20.glShaderSource(shader, shaderCode); 63 | GLES20.glCompileShader(shader); 64 | 65 | // return the shader 66 | return shader; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/performance/ReflectHelper.java: -------------------------------------------------------------------------------- 1 | package com.sample.performance; 2 | 3 | import android.util.Log; 4 | 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Method; 7 | 8 | public class ReflectHelper { 9 | 10 | public static Object getField(Class clazz, String fieldName, Object instance) { 11 | Field field; 12 | try { 13 | field = clazz.getDeclaredField(fieldName); 14 | field.setAccessible(true); 15 | return field.get(instance); 16 | } catch (Exception e) { 17 | e.printStackTrace(); 18 | return null; 19 | } 20 | } 21 | 22 | public static Object getField(String className, String fieldName, Object instance) { 23 | try { 24 | Class clazz = Class.forName(className); 25 | return getField(clazz, fieldName, instance); 26 | } catch (Exception e) { 27 | e.printStackTrace(); 28 | return null; 29 | } 30 | } 31 | 32 | public static void setField(Class clazz, String fieldName, Object value, Object instance) { 33 | Field field; 34 | try { 35 | field = clazz.getDeclaredField(fieldName); 36 | field.setAccessible(true); 37 | field.set(instance, value); 38 | } catch (Exception e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | 43 | public static void setField(String className, String fieldName, Object value, Object instance) { 44 | try { 45 | Class clazz = Class.forName(className); 46 | setField(clazz, fieldName, value, instance); 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | 50 | } 51 | } 52 | 53 | public static Object invokeMethod(Class clazz, String methodName, Object instance, Class[] paramTypes, Object[] params) { 54 | Method method; 55 | try { 56 | method = clazz.getDeclaredMethod(methodName, paramTypes); 57 | method.setAccessible(true); 58 | return method.invoke(instance, params); 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | return null; 62 | } 63 | } 64 | 65 | public static Object invokeMethod(String className, String methodName, Object instance, Class[] paramTypes, Object[] params) { 66 | try { 67 | Class clazz = Class.forName(className); 68 | return invokeMethod(clazz, methodName, instance, paramTypes, params); 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | return null; 72 | } 73 | } 74 | 75 | public static long getInstanceCount(Class find) { 76 | long start = System.currentTimeMillis(); 77 | long result = 0; 78 | try { 79 | Class clazz = Class.forName("dalvik.system.VMDebug"); 80 | Method method = clazz.getMethod("countInstancesOfClass", Class.class, boolean.class); 81 | result = (Long) method.invoke(null, find, false); 82 | } catch (Exception e) { 83 | e.printStackTrace(); 84 | result = 0; 85 | } 86 | Log.e("fuck", "use time===" + (System.currentTimeMillis() - start)); 87 | return result; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/performance/TooDeepViewStackException.java: -------------------------------------------------------------------------------- 1 | package com.sample.performance; 2 | 3 | public class TooDeepViewStackException extends RuntimeException 4 | { 5 | private static final long serialVersionUID = 9145361450217030369L; 6 | 7 | public TooDeepViewStackException(String detailMessage) 8 | { 9 | super(detailMessage); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/performance/ViewHericacy.java: -------------------------------------------------------------------------------- 1 | package com.sample.performance; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Application; 5 | import android.os.Build; 6 | 7 | /** 8 | * Created by clarkehe on 1/18/16. 9 | * Todo: 10 | */ 11 | public class ViewHericacy { 12 | 13 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 14 | public static void trackViewTreeDepth (Application app) 15 | { 16 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) 17 | { 18 | app.registerActivityLifecycleCallbacks(new ViewTreeTracer()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/plugin/activity/StubActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.plugin.activity; 2 | 3 | import android.app.Activity; 4 | 5 | /** 6 | * 原始的Activity; 这个Activity会在Manifest中注册; 7 | * 当启动这个Activity的时候, 我们会把它拦截;然后跳转到TargetActivity 8 | *

9 | * 用到插件里面的话,那么就是在宿主程序里面注册了一堆空的Activity 10 | *

11 | * 如果希望启动插件的Activity; 由于插件Activity没有在主程序的Manifest中注册 12 | * 因此直接启动肯定会问题(插件的Activity有可能在它自己的Manifest.xml 中注册 13 | * 但是由于插件并不是一个真正安装的程序, Android系统并不知道这件事 14 | *

15 | * 我们可以通过分析Activity的启动机制, 可以在"合适的时候" 进行偷梁换柱, 16 | * 虽然我们要启动TargetActivity; 但是我们在真正启动之前,暂时替换为RawActivity 17 | * 这样,就能绕过AMS的验证,最后真正启动的时候,我们再替换回来,保证启动的是我们自己 18 | *

19 | * 这样我们就成为了一个真正的Activity, 生命周期由系统管理! 20 | * Created by weishu on 16/1/7. 21 | */ 22 | public class StubActivity extends Activity { 23 | // dummy, just stub 24 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/plugin/activity/TargetActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.plugin.activity; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.widget.TextView; 7 | 8 | /** 9 | * 要注意的是,这个Activity并没有再Manifest中注册!!! 10 | *

11 | * 且看我们如何瞒天过海, 成功启动它. 12 | *

13 | * Created by weishu on 16/1/7. 14 | */ 15 | public class TargetActivity extends Activity { 16 | 17 | private static final String TAG = "TargetActivity"; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | Log.d(TAG, "onCreate() called with " + "savedInstanceState = [" + savedInstanceState + "]"); 23 | TextView tv = new TextView(this); 24 | tv.setText("TargetActivity 启动成功!!!"); 25 | setContentView(tv); 26 | 27 | } 28 | 29 | @Override 30 | protected void onPause() { 31 | super.onPause(); 32 | Log.d(TAG, "onPause() called with " + ""); 33 | } 34 | 35 | @Override 36 | protected void onResume() { 37 | super.onResume(); 38 | Log.d(TAG, "onResume() called with " + ""); 39 | } 40 | 41 | @Override 42 | protected void onStop() { 43 | super.onStop(); 44 | Log.d(TAG, "onStop() called with " + ""); 45 | } 46 | 47 | @Override 48 | protected void onDestroy() { 49 | super.onDestroy(); 50 | Log.d(TAG, "onDestroy() called with " + ""); 51 | } 52 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/plugin/activity/hook/AMSHookHelper.java: -------------------------------------------------------------------------------- 1 | package com.sample.plugin.activity.hook; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.lang.reflect.Proxy; 6 | 7 | import android.os.Handler; 8 | 9 | /** 10 | * @author weishu 11 | * @date 16/3/21 12 | */ 13 | public class AMSHookHelper { 14 | 15 | public static final String EXTRA_TARGET_INTENT = "extra_target_intent"; 16 | 17 | /** 18 | * Hook AMS 19 | *

20 | * 主要完成的操作是 "把真正要启动的Activity临时替换为在AndroidManifest.xml中声明的替身Activity" 21 | *

22 | * 进而骗过AMS 23 | * 24 | * @throws ClassNotFoundException 25 | * @throws NoSuchMethodException 26 | * @throws InvocationTargetException 27 | * @throws IllegalAccessException 28 | * @throws NoSuchFieldException 29 | */ 30 | public static void hookActivityManagerNative() throws ClassNotFoundException, 31 | NoSuchMethodException, InvocationTargetException, 32 | IllegalAccessException, NoSuchFieldException { 33 | 34 | // 17package android.util; 35 | // 18 36 | // 19/** 37 | // 20 * Singleton helper class for lazily initialization. 38 | // 21 * 39 | // 22 * Modeled after frameworks/base/include/utils/Singleton.h 40 | // 23 * 41 | // 24 * @hide 42 | // 25 */ 43 | // 26public abstract class Singleton { 44 | // 27 private T mInstance; 45 | // 28 46 | // 29 protected abstract T create(); 47 | // 30 48 | // 31 public final T get() { 49 | // 32 synchronized (this) { 50 | // 33 if (mInstance == null) { 51 | // 34 mInstance = create(); 52 | // 35 } 53 | // 36 return mInstance; 54 | // 37 } 55 | // 38 } 56 | // 39} 57 | // 40 58 | 59 | Class activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative"); 60 | 61 | Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault"); 62 | gDefaultField.setAccessible(true); 63 | 64 | Object gDefault = gDefaultField.get(null); 65 | 66 | // gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段 67 | Class singleton = Class.forName("android.util.Singleton"); 68 | Field mInstanceField = singleton.getDeclaredField("mInstance"); 69 | mInstanceField.setAccessible(true); 70 | 71 | // ActivityManagerNative 的gDefault对象里面原始的 IActivityManager对象 72 | Object rawIActivityManager = mInstanceField.get(gDefault); 73 | 74 | // 创建一个这个对象的代理对象, 然后替换这个字段, 让我们的代理对象帮忙干活 75 | Class iActivityManagerInterface = Class.forName("android.app.IActivityManager"); 76 | Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), 77 | new Class[]{iActivityManagerInterface}, new IActivityManagerHandler(rawIActivityManager)); 78 | mInstanceField.set(gDefault, proxy); 79 | 80 | } 81 | 82 | /** 83 | * 由于之前我们用替身欺骗了AMS; 现在我们要换回我们真正需要启动的Activity 84 | *

85 | * 不然就真的启动替身了, 狸猫换太子... 86 | *

87 | * 到最终要启动Activity的时候,会交给ActivityThread 的一个内部类叫做 H 来完成 88 | * H 会完成这个消息转发; 最终调用它的callback 89 | */ 90 | public static void hookActivityThreadHandler() throws Exception { 91 | 92 | // 先获取到当前的ActivityThread对象 93 | Class activityThreadClass = Class.forName("android.app.ActivityThread"); 94 | Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); 95 | currentActivityThreadField.setAccessible(true); 96 | Object currentActivityThread = currentActivityThreadField.get(null); 97 | 98 | // 由于ActivityThread一个进程只有一个,我们获取这个对象的mH 99 | Field mHField = activityThreadClass.getDeclaredField("mH"); 100 | mHField.setAccessible(true); 101 | Handler mH = (Handler) mHField.get(currentActivityThread); 102 | 103 | // 设置它的回调, 根据源码: 104 | // 我们自己给他设置一个回调,就会替代之前的回调; 105 | 106 | // public void dispatchMessage(Message msg) { 107 | // if (msg.callback != null) { 108 | // handleCallback(msg); 109 | // } else { 110 | // if (mCallback != null) { 111 | // if (mCallback.handleMessage(msg)) { 112 | // return; 113 | // } 114 | // } 115 | // handleMessage(msg); 116 | // } 117 | // } 118 | 119 | Field mCallBackField = Handler.class.getDeclaredField("mCallback"); 120 | mCallBackField.setAccessible(true); 121 | 122 | mCallBackField.set(mH, new ActivityThreadHandlerCallback(mH)); 123 | 124 | } 125 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/plugin/activity/hook/ActivityThreadHandlerCallback.java: -------------------------------------------------------------------------------- 1 | package com.sample.plugin.activity.hook; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import android.content.Intent; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | 9 | /** 10 | * @author weishu 11 | * @date 16/1/7 12 | */ 13 | /* package */ class ActivityThreadHandlerCallback implements Handler.Callback { 14 | 15 | Handler mBase; 16 | 17 | public ActivityThreadHandlerCallback(Handler base) { 18 | mBase = base; 19 | } 20 | 21 | @Override 22 | public boolean handleMessage(Message msg) { 23 | 24 | switch (msg.what) { 25 | // ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100 26 | // 本来使用反射的方式获取最好, 这里为了简便直接使用硬编码 27 | case 100: 28 | handleLaunchActivity(msg); 29 | break; 30 | } 31 | 32 | mBase.handleMessage(msg); 33 | return true; 34 | } 35 | 36 | private void handleLaunchActivity(Message msg) { 37 | // 这里简单起见,直接取出TargetActivity; 38 | 39 | Object obj = msg.obj; 40 | // 根据源码: 41 | // 这个对象是 ActivityClientRecord 类型 42 | // 我们修改它的intent字段为我们原来保存的即可. 43 | // switch (msg.what) { 44 | // case LAUNCH_ACTIVITY: { 45 | // Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); 46 | // final ActivityClientRecord r = (ActivityClientRecord) msg.obj; 47 | 48 | // r.packageInfo = getPackageInfoNoCheck( 49 | // r.activityInfo.applicationInfo, r.compatInfo); 50 | // handleLaunchActivity(r, null); 51 | 52 | 53 | try { 54 | // 把替身恢复成真身 55 | Field intent = obj.getClass().getDeclaredField("intent"); 56 | intent.setAccessible(true); 57 | Intent raw = (Intent) intent.get(obj); 58 | 59 | Intent target = raw.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT); 60 | raw.setComponent(target.getComponent()); 61 | 62 | } catch (NoSuchFieldException e) { 63 | e.printStackTrace(); 64 | } catch (IllegalAccessException e) { 65 | e.printStackTrace(); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/plugin/activity/hook/IActivityManagerHandler.java: -------------------------------------------------------------------------------- 1 | package com.sample.plugin.activity.hook; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | 6 | import android.content.ComponentName; 7 | import android.content.Intent; 8 | import android.util.Log; 9 | 10 | import com.sample.plugin.activity.StubActivity; 11 | 12 | 13 | /** 14 | * @author weishu 15 | * @dete 16/1/7. 16 | */ 17 | /* package */ class IActivityManagerHandler implements InvocationHandler { 18 | 19 | private static final String TAG = "IActivityManagerHandler"; 20 | 21 | Object mBase; 22 | 23 | public IActivityManagerHandler(Object base) { 24 | mBase = base; 25 | } 26 | 27 | @Override 28 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 29 | 30 | if ("startActivity".equals(method.getName())) { 31 | // 只拦截这个方法 32 | // 替换参数, 任你所为;甚至替换原始Activity启动别的Activity偷梁换柱 33 | // API 23: 34 | // public final Activity startActivityNow(Activity parent, String id, 35 | // Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state, 36 | // Activity.NonConfigurationInstances lastNonConfigurationInstances) { 37 | 38 | // 找到参数里面的第一个Intent 对象 39 | 40 | Intent raw; 41 | int index = 0; 42 | 43 | for (int i = 0; i < args.length; i++) { 44 | if (args[i] instanceof Intent) { 45 | index = i; 46 | break; 47 | } 48 | } 49 | raw = (Intent) args[index]; 50 | 51 | Intent newIntent = new Intent(); 52 | 53 | // 替身Activity的包名, 也就是我们自己的包名 54 | String stubPackage = "com.sample"; 55 | 56 | // 这里我们把启动的Activity临时替换为 StubActivity 57 | ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName()); 58 | newIntent.setComponent(componentName); 59 | 60 | // 把我们原始要启动的TargetActivity先存起来 61 | newIntent.putExtra(AMSHookHelper.EXTRA_TARGET_INTENT, raw); 62 | 63 | // 替换掉Intent, 达到欺骗AMS的目的 64 | args[index] = newIntent; 65 | 66 | Log.d(TAG, "hook success"); 67 | return method.invoke(mBase, args); 68 | } 69 | 70 | return method.invoke(mBase, args); 71 | } 72 | } -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/plugin/pluginInstall.java: -------------------------------------------------------------------------------- 1 | package com.sample.plugin; 2 | 3 | // 插件是独立APK, 需要自己安装 4 | 5 | public class pluginInstall { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/plugin/pluginLoad.java: -------------------------------------------------------------------------------- 1 | package com.sample.plugin; 2 | 3 | /** 4 | * Created by clarkehe on 29/7/16. 5 | */ 6 | public class pluginLoad { 7 | } 8 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/record/RecordActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.record; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.android.grafika.gles.EglCore; 8 | import com.android.grafika.gles.FullFrameRect; 9 | import com.android.grafika.gles.OffscreenSurface; 10 | import com.android.grafika.gles.Texture2dProgram; 11 | import com.sample.R; 12 | import com.tencent.qqgamemi.srp.media.newapi.ScreenRecorderManager; 13 | 14 | public class RecordActivity extends Activity { 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_record); 20 | 21 | findViewById(R.id.butStart).setOnClickListener(new View.OnClickListener() { 22 | @Override 23 | public void onClick(View v) { 24 | 25 | test(); 26 | 27 | // boolean ret = ScreenCapture.requestPermission(RecordActivity.this, new Runnable() { 28 | // @Override 29 | // public void run() { 30 | // ScreenRecorderManager.getInstance().startRecord(RecordActivity.this, 31 | // ScreenCapture.gerResultCode(), ScreenCapture.getData()); 32 | // } 33 | // }); 34 | // 35 | // if (ret){ 36 | // ScreenRecorderManager.getInstance().startRecord(RecordActivity.this, 37 | // ScreenCapture.gerResultCode(), ScreenCapture.getData()); 38 | // } 39 | } 40 | }); 41 | 42 | findViewById(R.id.butStop).setOnClickListener(new View.OnClickListener() { 43 | @Override 44 | public void onClick(View v) { 45 | 46 | ScreenRecorderManager.getInstance().stopRecord(RecordActivity.this); 47 | } 48 | }); 49 | } 50 | 51 | private void test() { 52 | final int width = 100; 53 | final int height = 100; 54 | 55 | EglCore mEglCore = new EglCore(null, EglCore.FLAG_RECORDABLE); 56 | 57 | //mOffscreenSurface在此处作用??? 58 | OffscreenSurface mOffscreenSurface = new OffscreenSurface(mEglCore, width, height); 59 | mOffscreenSurface.makeCurrent(); 60 | 61 | FullFrameRect mFullFrameBlit = new FullFrameRect(new Texture2dProgram(Texture2dProgram.ProgramType.TEXTURE_EXT)); 62 | int mTextureId = mFullFrameBlit.createTextureObject(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/record/RecordUtil.java: -------------------------------------------------------------------------------- 1 | package com.sample.record; 2 | 3 | import android.os.Environment; 4 | 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | 8 | /** 9 | * Created by clarkehe on 5/4/16. 10 | * Todo: 11 | */ 12 | public class RecordUtil { 13 | 14 | public static final String getVideoPath() { 15 | return getVideoRootPath() + "ScreenRecord"; 16 | } 17 | 18 | public static final String getVideoRootPath() { 19 | return Environment.getExternalStorageDirectory().getAbsolutePath() 20 | + "/Tencent/shouyoubao/"; 21 | } 22 | 23 | public static String getRecordVideoPath() { 24 | SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); 25 | String fileName = sDateFormat.format(new Date(System.currentTimeMillis())) + "-" 26 | + "test"; 27 | 28 | return getVideoPath() + "/" + fileName + ".mp4"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/record/ScreenCapture.java: -------------------------------------------------------------------------------- 1 | package com.sample.record; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.media.projection.MediaProjectionManager; 8 | import android.os.Bundle; 9 | import android.util.Log; 10 | 11 | 12 | /** 13 | * Created by clarkehe on 3/24/16. 14 | * 5.0及以上申请录屏的权限 15 | */ 16 | public class ScreenCapture { 17 | 18 | private final static String TAG = "ScreenCapture"; 19 | private final static int REQUEST_CODE_SCREEN_CAPTURE = 1; 20 | 21 | private static boolean sGetPermissionSuccess = false; 22 | private static Runnable sRunnable; 23 | 24 | private static int sResultCode = 0; 25 | private static Intent sData = null; 26 | 27 | public static class TemActivity extends Activity { 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | requestScreenCapture(this); 32 | } 33 | 34 | @Override 35 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 36 | Log.i(TAG, "onActivityResult, requestCode:" + requestCode + ", resultCode:" + requestCode); 37 | super.onActivityResult(requestCode, resultCode, data); 38 | if (requestCode == REQUEST_CODE_SCREEN_CAPTURE) { 39 | if (resultCode == Activity.RESULT_OK) { 40 | sGetPermissionSuccess = true; 41 | 42 | sResultCode = resultCode; 43 | sData = data; 44 | 45 | new android.os.Handler().post(sRunnable); 46 | 47 | // SDKApiHelper.getInstance().runOnMainThread(sRunnable); 48 | // sRunnable = null; 49 | 50 | // QmiSdkApi.setScreenCaptureIntent(resultCode, data); 51 | } 52 | } 53 | finish(); 54 | } 55 | } 56 | 57 | public static boolean requestPermission(Context context, Runnable runnable) { 58 | if (sGetPermissionSuccess) return true; 59 | sRunnable = runnable; 60 | 61 | final Intent intent = new Intent(context, TemActivity.class); 62 | if (!(context instanceof Activity)) { 63 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 64 | } 65 | context.startActivity(intent); 66 | return false; 67 | } 68 | 69 | public static int gerResultCode() { 70 | return sResultCode; 71 | } 72 | 73 | public static Intent getData() { 74 | return sData; 75 | } 76 | 77 | @TargetApi(21) 78 | static void requestScreenCapture(final Activity context) { 79 | final MediaProjectionManager manager = (MediaProjectionManager) context.getSystemService(Context.MEDIA_PROJECTION_SERVICE); 80 | final Intent permissionIntent = manager.createScreenCaptureIntent(); 81 | context.startActivityForResult(permissionIntent, REQUEST_CODE_SCREEN_CAPTURE); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/res_confuse/GetRes.java: -------------------------------------------------------------------------------- 1 | package com.sample.res_confuse; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | 6 | import com.sample.R; 7 | 8 | import java.lang.reflect.Field; 9 | 10 | /** 11 | * 资源会混淆,有些资源引用或访问方式在混淆后会失效,需要注意。 12 | */ 13 | public class GetRes { 14 | 15 | //在代码或资源中直接引用资源ID, 应该是不会有影响的。 16 | //要注意下面的两种方式: 17 | 18 | //1 不是修改源码混淆 19 | //2 R文件还是原ID, 没有混淆ID 20 | private int getStringResourceId(String fileName) { 21 | Field field = null; 22 | try { 23 | field = R.string.class.getField(fileName); 24 | } catch (NoSuchFieldException e) { 25 | return 0; 26 | } 27 | try { 28 | return field.getInt(null); 29 | } catch (IllegalAccessException e) { 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | //读res文件夹及resources.arsc文件?? 36 | private int getResId(Context context, String name) { 37 | Resources resources = context.getResources(); 38 | return resources.getIdentifier(name, "string", context.getPackageName()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/sleep/SampleBootReceiver.java: -------------------------------------------------------------------------------- 1 | package com.sample.sleep; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | /** 8 | * This BroadcastReceiver automatically (re)starts the alarm when the device is 9 | * rebooted. This receiver is set to be disabled (android:enabled="false") in the 10 | * application's manifest file. When the user sets the alarm, the receiver is enabled. 11 | * When the user cancels the alarm, the receiver is disabled, so that rebooting the 12 | * device will not trigger this receiver. 13 | */ 14 | // BEGIN_INCLUDE(autostart) 15 | public class SampleBootReceiver extends BroadcastReceiver { 16 | SampleAlarmReceiver alarm = new SampleAlarmReceiver(); 17 | 18 | @Override 19 | public void onReceive(Context context, Intent intent) { 20 | if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { 21 | alarm.setAlarm(context); 22 | } 23 | } 24 | } 25 | //END_INCLUDE(autostart) 26 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/sleep/SampleSchedulingService.java: -------------------------------------------------------------------------------- 1 | package com.sample.sleep; 2 | 3 | import android.app.IntentService; 4 | import android.app.NotificationManager; 5 | import android.app.PendingIntent; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.support.v4.app.NotificationCompat; 9 | import android.util.Log; 10 | 11 | import com.sample.R; 12 | 13 | import java.io.BufferedReader; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.InputStreamReader; 17 | import java.net.HttpURLConnection; 18 | import java.net.URL; 19 | 20 | /** 21 | * This {@code IntentService} does the app's actual work. 22 | * {@code SampleAlarmReceiver} (a {@code WakefulBroadcastReceiver}) holds a 23 | * partial wake lock for this service while the service does its work. When the 24 | * service is finished, it calls {@code completeWakefulIntent()} to release the 25 | * wake lock. 26 | */ 27 | public class SampleSchedulingService extends IntentService { 28 | public SampleSchedulingService() { 29 | super("SchedulingService"); 30 | } 31 | 32 | public static final String TAG = "Scheduling Demo"; 33 | // An ID used to post the notification. 34 | public static final int NOTIFICATION_ID = 1; 35 | // The string the app searches for in the Google home page content. If the app finds 36 | // the string, it indicates the presence of a doodle. 37 | public static final String SEARCH_STRING = "doodle"; 38 | // The Google home page URL from which the app fetches content. 39 | // You can find a list of other Google domains with possible doodles here: 40 | // http://en.wikipedia.org/wiki/List_of_Google_domains 41 | public static final String URL = "http://www.google.com"; 42 | private NotificationManager mNotificationManager; 43 | NotificationCompat.Builder builder; 44 | 45 | @Override 46 | protected void onHandleIntent(Intent intent) { 47 | // BEGIN_INCLUDE(service_onhandle) 48 | // The URL from which to fetch content. 49 | String urlString = URL; 50 | 51 | String result = ""; 52 | 53 | // Try to connect to the Google homepage and download content. 54 | try { 55 | result = loadFromNetwork(urlString); 56 | } catch (IOException e) { 57 | Log.i(TAG, "getString(R.string.connection_error)"); 58 | } 59 | 60 | // If the app finds the string "doodle" in the Google home page content, it 61 | // indicates the presence of a doodle. Post a "Doodle Alert" notification. 62 | if (result.indexOf(SEARCH_STRING) != -1) { 63 | sendNotification("getString(R.string.doodle_found)"); 64 | Log.i(TAG, "Found doodle!!"); 65 | } else { 66 | sendNotification("getString(R.string.no_doodle)"); 67 | Log.i(TAG, "No doodle found. :-("); 68 | } 69 | // Release the wake lock provided by the BroadcastReceiver. 70 | //SampleAlarmReceiver.completeWakefulIntent(intent); 71 | // END_INCLUDE(service_onhandle) 72 | } 73 | 74 | // Post a notification indicating whether a doodle was found. 75 | private void sendNotification(String msg) { 76 | mNotificationManager = (NotificationManager) 77 | this.getSystemService(Context.NOTIFICATION_SERVICE); 78 | 79 | PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 80 | new Intent(this, TestSleepActivity.class), 0); 81 | 82 | NotificationCompat.Builder mBuilder = 83 | new NotificationCompat.Builder(this) 84 | .setSmallIcon(R.drawable.ic_launcher) 85 | .setContentTitle("getString(R.string.doodle_alert)") 86 | .setStyle(new NotificationCompat.BigTextStyle() 87 | .bigText(msg)) 88 | .setContentText(msg); 89 | 90 | mBuilder.setContentIntent(contentIntent); 91 | mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); 92 | } 93 | 94 | // 95 | // The methods below this line fetch content from the specified URL and return the 96 | // content as a string. 97 | // 98 | 99 | /** 100 | * Given a URL string, initiate a fetch operation. 101 | */ 102 | private String loadFromNetwork(String urlString) throws IOException { 103 | InputStream stream = null; 104 | String str = ""; 105 | 106 | try { 107 | stream = downloadUrl(urlString); 108 | str = readIt(stream); 109 | } finally { 110 | if (stream != null) { 111 | stream.close(); 112 | } 113 | } 114 | return str; 115 | } 116 | 117 | /** 118 | * Given a string representation of a URL, sets up a connection and gets 119 | * an input stream. 120 | * 121 | * @param urlString A string representation of a URL. 122 | * @return An InputStream retrieved from a successful HttpURLConnection. 123 | * @throws IOException 124 | */ 125 | private InputStream downloadUrl(String urlString) throws IOException { 126 | 127 | URL url = new URL(urlString); 128 | HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 129 | conn.setReadTimeout(10000 /* milliseconds */); 130 | conn.setConnectTimeout(15000 /* milliseconds */); 131 | conn.setRequestMethod("GET"); 132 | conn.setDoInput(true); 133 | // Start the query 134 | conn.connect(); 135 | InputStream stream = conn.getInputStream(); 136 | return stream; 137 | } 138 | 139 | /** 140 | * Reads an InputStream and converts it to a String. 141 | * 142 | * @param stream InputStream containing HTML from www.google.com. 143 | * @return String version of InputStream. 144 | * @throws IOException 145 | */ 146 | private String readIt(InputStream stream) throws IOException { 147 | 148 | StringBuilder builder = new StringBuilder(); 149 | BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); 150 | for (String line = reader.readLine(); line != null; line = reader.readLine()) 151 | builder.append(line); 152 | reader.close(); 153 | return builder.toString(); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/sleep/TestSleepActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.sleep; 2 | 3 | import android.app.Activity; 4 | import android.app.AlarmManager; 5 | import android.app.PendingIntent; 6 | import android.content.BroadcastReceiver; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.net.Uri; 10 | import android.os.Bundle; 11 | import android.os.Environment; 12 | import android.os.Handler; 13 | import android.os.HandlerThread; 14 | import android.os.Looper; 15 | import android.os.Message; 16 | import android.os.SystemClock; 17 | import android.util.Log; 18 | import android.widget.MediaController; 19 | import android.widget.VideoView; 20 | 21 | import com.sample.R; 22 | 23 | import java.util.Calendar; 24 | 25 | public class TestSleepActivity extends Activity { 26 | 27 | private static String TAG = "TestSleepActivity"; 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_test_sleep); 33 | } 34 | 35 | @Override 36 | protected void onResume() { 37 | super.onResume(); 38 | 39 | //postDelayedMsg(); 40 | 41 | setAlarm(this); 42 | 43 | // createThread(); 44 | 45 | // createThread1(); 46 | 47 | //playVideo(); 48 | } 49 | 50 | private void playVideo() { 51 | Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath() + "/test.mp4"); 52 | Log.d(TAG, uri.toString()); 53 | VideoView videoView = (VideoView) this.findViewById(R.id.videoView); 54 | videoView.setMediaController(new MediaController(this)); 55 | videoView.setVideoURI(uri); 56 | videoView.start(); 57 | videoView.requestFocus(); 58 | } 59 | 60 | private void createThread1() { 61 | new Thread(new Runnable() { 62 | @Override 63 | public void run() { 64 | while (true) { 65 | try { 66 | Thread.sleep(10 * 1000); 67 | } catch (Exception e) { 68 | 69 | } 70 | Log.d(TAG, "Delayed Msg."); 71 | } 72 | } 73 | }).start(); 74 | } 75 | 76 | private void createThread() { 77 | HandlerThread thread = new HandlerThread("test"); 78 | thread.start(); 79 | 80 | Handler handler = new Handler(thread.getLooper()) { 81 | @Override 82 | public void handleMessage(Message msg) { 83 | super.handleMessage(msg); 84 | } 85 | }; 86 | 87 | postDelayedMsg(handler); 88 | } 89 | 90 | private void postDelayedMsg(final Handler handler) { 91 | handler.postDelayed(new Runnable() { 92 | @Override 93 | public void run() { 94 | Log.d(TAG, "Delayed Msg."); 95 | postDelayedMsg(handler); 96 | } 97 | }, 10 * 1000); 98 | } 99 | 100 | private void postDelayedMsg() { 101 | Handler mHandler = new Handler(Looper.getMainLooper()); 102 | 103 | Runnable mRunnable = new Runnable() { 104 | @Override 105 | public void run() { 106 | Log.d(TAG, "Delayed Msg."); 107 | } 108 | }; 109 | 110 | long delay = 5 * 60 * 1000; 111 | mHandler.postDelayed(mRunnable, delay); 112 | } 113 | 114 | static void setAlarm(Context context) { 115 | AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 116 | 117 | // Intent intent = new Intent(context, SimpleBroadCastReceiver.class); 118 | Intent intent = new Intent("com.sample.alarm"); 119 | PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 120 | 121 | long delay = 10 * 1000; 122 | alarmMgr.setExact(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + delay, alarmIntent); 123 | } 124 | 125 | public static class SimpleBroadCastReceiver extends BroadcastReceiver { 126 | @Override 127 | public void onReceive(Context context, Intent intent) { 128 | Log.d(TAG, "SimpleBroadCastReceiver#onReceive"); 129 | //setAlarm(SimpleBroadCastReceiver.this); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/statsUsage/StatsUsageActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.statsUsage; 2 | 3 | import android.app.Activity; 4 | import android.app.ActivityManager; 5 | import android.app.usage.UsageEvents; 6 | import android.app.usage.UsageStatsManager; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.os.Build; 10 | import android.os.Bundle; 11 | import android.provider.Settings; 12 | import android.util.Log; 13 | import android.view.View; 14 | 15 | import com.sample.R; 16 | 17 | import java.util.List; 18 | 19 | public class StatsUsageActivity extends Activity { 20 | 21 | private final String TAG = this.getClass().getSimpleName(); 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_stats_usage); 27 | 28 | findViewById(R.id.getButton).setOnClickListener(new View.OnClickListener() { 29 | @Override 30 | public void onClick(View v) { 31 | start(); 32 | } 33 | }); 34 | 35 | findViewById(R.id.getTopApp).setOnClickListener(new View.OnClickListener() { 36 | @Override 37 | public void onClick(View v) { 38 | getTop(); 39 | } 40 | }); 41 | } 42 | 43 | private void start() { 44 | Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); 45 | this.startActivity(intent); 46 | } 47 | 48 | private void getTop() { 49 | new android.os.Handler().postDelayed(new Runnable() { 50 | @Override 51 | public void run() { 52 | 53 | final String topApp = getLauncherTopApp(StatsUsageActivity.this); 54 | Log.e(TAG, "topApp:" + topApp); 55 | 56 | getTop(); 57 | } 58 | }, 500); 59 | } 60 | 61 | static UsageStatsManager sUsageStatsManager = null; 62 | 63 | public static String getLauncherTopApp(Context context) { 64 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 65 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 66 | List appTasks = activityManager.getRunningTasks(1); 67 | if (null != appTasks && !appTasks.isEmpty()) { 68 | return appTasks.get(0).topActivity.getPackageName(); 69 | } 70 | } else { 71 | long endTime = System.currentTimeMillis(); 72 | long beginTime = endTime - 10000; 73 | if (sUsageStatsManager == null) { 74 | //sUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); 75 | } 76 | String result = ""; 77 | UsageEvents.Event event = new UsageEvents.Event(); 78 | UsageEvents usageEvents = sUsageStatsManager.queryEvents(beginTime, endTime); 79 | while (usageEvents.hasNextEvent()) { 80 | usageEvents.getNextEvent(event); 81 | if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) { 82 | result = event.getPackageName(); 83 | } 84 | } 85 | if (!android.text.TextUtils.isEmpty(result)) { 86 | return result; 87 | } 88 | } 89 | return ""; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/java/com/sample/web/WebViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.sample.web; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Bitmap; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.Window; 8 | import android.webkit.JsPromptResult; 9 | import android.webkit.WebChromeClient; 10 | import android.webkit.WebResourceResponse; 11 | import android.webkit.WebView; 12 | import android.webkit.WebViewClient; 13 | 14 | import com.sample.BaseActivity; 15 | import com.sample.R; 16 | 17 | /** 18 | * Created by clarkehe on 1/18/16. 19 | * Todo: 20 | */ 21 | public class WebViewActivity extends BaseActivity { 22 | 23 | private static final String TAG = "WebViewActivity"; 24 | 25 | WebView mWebView; 26 | 27 | public WebViewActivity() { 28 | super(); 29 | } 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | 35 | // Let's display the progress in the activity title bar, like the 36 | // browser app does. 37 | getWindow().requestFeature(Window.FEATURE_PROGRESS); 38 | 39 | setContentView(R.layout.webview_activity); 40 | 41 | mWebView = (WebView)findViewById(R.id.webView); 42 | final WebView webView = mWebView; 43 | 44 | // 不设置,loadUrl会启动浏览器 45 | mWebView.setWebViewClient(new MyWebViewClient()); 46 | 47 | // 48 | mWebView.setWebChromeClient(new MyWebChromeClient()); 49 | 50 | // JS 51 | mWebView.getSettings().setJavaScriptEnabled(true); 52 | 53 | // mWebView.loadUrl("http://baidu.com"); 54 | mWebView.loadUrl("file:///android_asset/www/index.html"); 55 | 56 | //Load JS after Page Loaded 57 | //NATIVE CALL JS 58 | new android.os.Handler().postDelayed(new Runnable() { 59 | @Override 60 | public void run() { 61 | //. NATIVE CALL JS, 没有返回值。 62 | //webView.loadUrl("javascript:jsFun('Hello World!')"); 63 | //webView.evaluateJavascript(); 64 | } 65 | }, 1000); 66 | 67 | //1. JS CALL NATIVE. 不安全,有返回值。 68 | // webView.addJavascriptInterface(new JsObject(), "injectedObject"); 69 | // webView.loadData("", "text/html", null); 70 | // webView.loadUrl("javascript:alert(injectedObject.toString())"); 71 | } 72 | 73 | // class JsObject { 74 | // @JavascriptInterface 75 | // public String toString() { return "injectedObject"; } 76 | // } 77 | 78 | @Override 79 | public void onBackPressed() { 80 | if (mWebView.canGoBack()) { 81 | mWebView.goBack(); 82 | } else { 83 | super.onBackPressed(); 84 | } 85 | } 86 | 87 | private class MyWebViewClient extends WebViewClient 88 | { 89 | //JSBridge 2. JS CALL NATIVE,没有返回值 90 | //2. WX,使用的方式是在JS中存一个回CALLBACK, NATIVE来调用。 91 | @Override 92 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 93 | Log.d(TAG, "shouldOverrideUrlLoading, url:" + url); 94 | if (url.contains("jsbridge:")){ 95 | return true; 96 | } 97 | return false; 98 | } 99 | 100 | //Load Native Cache Res 101 | @Override 102 | public WebResourceResponse shouldInterceptRequest(WebView view, String url) { 103 | Log.d(TAG, "shouldInterceptRequest, url:" + url); 104 | return super.shouldInterceptRequest(view, url); 105 | } 106 | 107 | @Override 108 | public void onPageFinished(WebView view, String url) { 109 | super.onPageFinished(view, url); 110 | } 111 | 112 | @Override 113 | public void onPageStarted(WebView view, String url, Bitmap favicon) { 114 | super.onPageStarted(view, url, favicon); 115 | } 116 | } 117 | 118 | class MyWebChromeClient extends WebChromeClient 119 | { 120 | // 3. JS CALL NATIVE, 有返回值。 121 | @Override 122 | public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { 123 | 124 | Log.d(TAG, "onJsPrompt, url:" + url); 125 | Log.d(TAG, "onJsPrompt, message:" + message); 126 | Log.d(TAG, "onJsPrompt, defaultValue:" + defaultValue); 127 | 128 | if (message.contains("jsbridge:")){ 129 | result.confirm("return"); 130 | return true; 131 | } 132 | 133 | return super.onJsPrompt(view, url, message, defaultValue, result); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/jniLibs/apatch-debug.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clarkehe/Android/ebb4a4427d6e4e9d37e896841aa93f9c6ee2b1f3/code/Sample/app/src/main/jniLibs/apatch-debug.aar -------------------------------------------------------------------------------- /code/Sample/app/src/main/res/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clarkehe/Android/ebb4a4427d6e4e9d37e896841aa93f9c6ee2b1f3/code/Sample/app/src/main/res/drawable/ic_launcher.png -------------------------------------------------------------------------------- /code/Sample/app/src/main/res/drawable/startup_twinkle_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/res/drawable/ui_selector_iconfont_textview_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /code/Sample/app/src/main/res/layout/activity_animator.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 |