{"content":"Command: cd /Users/nickchen/WorkBuddy/2026-10-23-15-17-57/cleanyourtv && sed -n '1700,1860p' launcher/app/src/main/java/com/gathin/tvhome/MainActivity.java\nStdout:     }\r\n\r\n    // ---- cinematic shelf -------------------------------------------------------------------------\r\n\r\n    /**\r\n     * Local fork: forceHasOverlappingRendering is API 24, so calling it on Android 6/7 throws\r\n     * NoSuchMethodError at launch. It is a rendering optimisation only (skip the offscreen buffer for a\r\n     * view whose alpha is applied while drawing), so leaving it out below 24 costs a little memory on\r\n     * fades and changes nothing else.\r\n     */\r\n    private static void noOverlappingRendering(View v) {\r\n        if (Build.VERSION.SDK_INT >= 24) v.forceHasOverlappingRendering(false);\r\n    }\r\n\r\n    /** Local fork: TvInputInfo#isHidden is API 24; nothing is hidden on Android 6/7. */\r\n    private boolean inputHidden(TvInputInfo info) {\r\n        return Build.VERSION.SDK_INT >= 24 && info.isHidden(this);\r\n    }\r\n\r\n    /** Local fork: TvInputInfo#loadCustomLabel is API 24; Android 6/7 inputs have no owner label. */\r\n    private CharSequence inputCustomLabel(TvInputInfo info) {\r\n        return Build.VERSION.SDK_INT >= 24 ? info.loadCustomLabel(this) : null;\r\n    }\r\n\r\n    /**\r\n     * The parts that never change: a larger name, the meta line and the dots, the artwork and the scrim, at\r\n     * scrimPercent of its strength (cinematic_scrim). cinematic_art 0 leaves the artwork out, 0 percent the scrim.\r\n     */\r\n    private void setUpCinematicShelf(int scrimPercent) {\r\n        heroName.setTextSize(TypedValue.COMPLEX_UNIT_SP, 68f);\r\n        heroName.setLetterSpacing(-0.025f);\r\n        heroMeta.setVisibility(View.INVISIBLE);   // keeps its height, so the name does not jump when it fills in\r\n        heroDots.setVisibility(heroDotsShown ? View.VISIBLE : View.GONE);\r\n        heroArt.setVisibility(heroArtAlpha > 0f ? View.VISIBLE : View.GONE);\r\n        // One stretched bitmap: apply its alpha while drawing instead of through an offscreen buffer.\r\n        noOverlappingRendering(heroArt);\r\n        if (scrimPercent <= 0) return;   // cinematic_scrim 0: no scrim at all\r\n        View scrim = findViewById(R.id.heroScrim);\r\n        Drawable shade = scrimDrawable();\r\n        // On the bitmap's paint, not the view: a view's alpha would draw the full screen through an offscreen buffer.\r\n        shade.setAlpha(scrimPercent * 255 / 100);\r\n        scrim.setBackground(shade);\r\n        scrim.setVisibility(View.VISIBLE);\r\n    }\r\n\r\n    /**\r\n     * Darkens the side the shelf's text is on and the lower part of the screen, so the name, the dock and\r\n     * the grid stay readable over the artwork. One small bitmap stretched over the screen, like the glow.\r\n     */\r\n    private Drawable scrimDrawable() {\r\n        int w = 480, h = 270;\r\n        Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);\r\n        Canvas canvas = new Canvas(bmp);\r\n        boolean rtl = getResources().getBoolean(R.bool.rtl_menu_bar);\r\n        Paint paint = new Paint();\r\n        paint.setShader(new LinearGradient(rtl ? w : 0, 0, rtl ? 0 : w, 0,\r\n                new int[]{0xD1060406, 0x59060406, 0x00060406}, new float[]{0f, 0.42f, 0.7f}, Shader.TileMode.CLAMP));\r\n        canvas.drawRect(0, 0, w, h, paint);\r\n        paint.setShader(new LinearGradient(0, 0, 0, h,\r\n                new int[]{0x00060406, 0x00060406, 0xDB060406, 0xFF060406}, new float[]{0f, 0.28f, 0.58f, 0.76f},\r\n                Shader.TileMode.CLAMP));\r\n        canvas.drawRect(0, 0, w, h, paint);\r\n        BitmapDrawable scrim = new BitmapDrawable(getResources(), bmp);\r\n        scrim.setGravity(Gravity.FILL);\r\n        return scrim;\r\n    }\r\n\r\n    /** Swaps the backdrop to this app's artwork: fade out, swap, fade in, the way the shelf's name changes. */\r\n    private void showHeroArt(int position) {\r\n        String key = adapter.keyAt(position);\r\n        if (key == null) return;\r\n        heroArt.animate().cancel();\r\n        if (key.equals(heroArtKey)) {\r\n            fadeHeroArt(420);\r\n            return;\r\n        }\r\n        Bitmap art = heroArtCache.get(key);\r\n        if (art == null) {\r\n            art = HeroArt.from(adapter.artworkAt(position));\r\n            if (art != null) heroArtCache.put(key, art);\r\n        }\r\n        heroArtKey = key;\r\n        final Bitmap next = art;\r\n        if (!animations || reducedMotion || heroArt.getAlpha() == 0f) {\r\n            heroArt.setImageBitmap(next);\r\n            fadeHeroArt(600);\r\n            return;\r\n        }\r\n        heroArt.animate().alpha(0f).setDuration(140).withEndAction(() -> {\r\n            heroArt.setImageBitmap(next);\r\n            heroArt.animate().alpha(heroArtAlpha).setDuration(420).start();\r\n        }).start();\r\n    }\r\n\r\n    private void fadeHeroArt(int ms) {\r\n        if (!animations) heroArt.setAlpha(heroArtAlpha);\r\n        else heroArt.animate().alpha(heroArtAlpha).setDuration(reducedMotion ? Math.min(ms, Motion.REDUCED_MS) : ms).start();\r\n    }\r\n\r\n    /** \"Last opened yesterday, 9:40 PM\", the time in white; nothing (but the same height) when it was never seen. */\r\n    private void updateHeroMeta(int position) {\r\n        long when = LastOpened.get(this, adapter.packageAt(position));\r\n        if (when <= 0) {\r\n            heroMeta.setVisibility(View.INVISIBLE);\r\n            return;\r\n        }\r\n        String rel = LastOpened.describe(this, when);\r\n        String text = getString(R.string.last_opened, rel);\r\n        SpannableString line = new SpannableString(text);\r\n        int at = text.indexOf(rel);\r\n        if (at >= 0) line.setSpan(new ForegroundColorSpan(0xFFFFFFFF), at, at + rel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\r\n        heroMeta.setText(line);\r\n        heroMeta.setVisibility(View.VISIBLE);\r\n    }\r\n\r\n    /** One dot per dock app, the focused one drawn long and white. Hebrew docks start on the right, so do the dots. */\r\n    private void updateHeroDots(int position) {\r\n        if (!heroDotsShown) return;\r\n        int count = Math.min(adapter.dockFilled(), COLUMNS);\r\n        heroDots.setVisibility(count > 1 ? View.VISIBLE : View.INVISIBLE);\r\n        if (heroDots.getChildCount() != count) {\r\n            heroDots.removeAllViews();\r\n            for (int i = 0; i < count; i++) {\r\n                View dot = new View(this);\r\n                GradientDrawable bg = new GradientDrawable();\r\n                bg.setCornerRadius(3 * density);\r\n                dot.setBackground(bg);\r\n                LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams((int) (6 * density), (int) (6 * density));\r\n                if (i > 0) lp.leftMargin = (int) (6 * density);\r\n                heroDots.addView(dot, lp);\r\n            }\r\n        }\r\n        for (int i = 0; i < count; i++) {\r\n            View dot = heroDots.getChildAt(rtlAppGrid ? count - 1 - i : i);\r\n            boolean on = i == position;\r\n            ((GradientDrawable) dot.getBackground()).setColor(on ? 0xFFFFFFFF : 0x4DFFFFFF);\r\n            ViewGroup.LayoutParams lp = dot.getLayoutParams();\r\n            int w = (int) ((on ? 20 : 6) * density);\r\n            if (lp.width != w) {\r\n                lp.width = w;\r\n                dot.setLayoutParams(lp);\r\n            }\r\n        }\r\n    }\r\n\r\n    /**\r\n     * The glow used to be a full-screen radial GradientDrawable. On Android 10 that drawable is\r\n     * drawn on the CPU, so fading it for half a second redrew 1920×1080 every frame and the\r\n     * highlight looked like it was running at a few frames per second. A small bitmap stretched\r\n     * over the screen is one texture. It is painted once in white and tinted with the app's colour,\r\n     * so changing apps neither repaints it on the CPU (about 11 ms) nor uploads it again.\r\n     */\r\n    private void setAmbient(int color) {\r\n        if (color == ambientColor && ambient.getBackground() != null) return;\r\n        ambientColor = color;\r\n        BitmapDrawable glow = (BitmapDrawable) ambient.getBackground();\r\n        if (glow == null) {\r\n            int w = 480;\r\n            int h = 270;\r\n            Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);\r\n            Canvas canvas = new Canvas(bmp);\r\n\nStderr: (empty)\nExit Code: 0\nSignal: (none)","renderer":{"type":"text","value":"    }\r\n\r\n    // ---- cinematic shelf -------------------------------------------------------------------------\r\n\r\n    /**\r\n     * Local fork: forceHasOverlappingRendering is API 24, so calling it on Android 6/7 throws\r\n     * NoSuchMethodError at launch. It is a rendering optimisation only (skip the offscreen buffer for a\r\n     * view whose alpha is applied while drawing), so leaving it out below 24 costs a little memory on\r\n     * fades and changes nothing else.\r\n     */\r\n    private static void noOverlappingRendering(View v) {\r\n        if (Build.VERSION.SDK_INT >= 24) v.forceHasOverlappingRendering(false);\r\n    }\r\n\r\n    /** Local fork: TvInputInfo#isHidden is API 24; nothing is hidden on Android 6/7. */\r\n    private boolean inputHidden(TvInputInfo info) {\r\n        return Build.VERSION.SDK_INT >= 24 && info.isHidden(this);\r\n    }\r\n\r\n    /** Local fork: TvInputInfo#loadCustomLabel is API 24; Android 6/7 inputs have no owner label. */\r\n    private CharSequence inputCustomLabel(TvInputInfo info) {\r\n        return Build.VERSION.SDK_INT >= 24 ? info.loadCustomLabel(this) : null;\r\n    }\r\n\r\n    /**\r\n     * The parts that never change: a larger name, the meta line and the dots, the artwork and the scrim, at\r\n     * scrimPercent of its strength (cinematic_scrim). cinematic_art 0 leaves the artwork out, 0 percent the scrim.\r\n     */\r\n    private void setUpCinematicShelf(int scrimPercent) {\r\n        heroName.setTextSize(TypedValue.COMPLEX_UNIT_SP, 68f);\r\n        heroName.setLetterSpacing(-0.025f);\r\n        heroMeta.setVisibility(View.INVISIBLE);   // keeps its height, so the name does not jump when it fills in\r\n        heroDots.setVisibility(heroDotsShown ? View.VISIBLE : View.GONE);\r\n        heroArt.setVisibility(heroArtAlpha > 0f ? View.VISIBLE : View.GONE);\r\n        // One stretched bitmap: apply its alpha while drawing instead of through an offscreen buffer.\r\n        noOverlappingRendering(heroArt);\r\n        if (scrimPercent <= 0) return;   // cinematic_scrim 0: no scrim at all\r\n        View scrim = findViewById(R.id.heroScrim);\r\n        Drawable shade = scrimDrawable();\r\n        // On the bitmap's paint, not the view: a view's alpha would draw the full screen through an offscreen buffer.\r\n        shade.setAlpha(scrimPercent * 255 / 100);\r\n        scrim.setBackground(shade);\r\n        scrim.setVisibility(View.VISIBLE);\r\n    }\r\n\r\n    /**\r\n     * Darkens the side the shelf's text is on and the lower part of the screen, so the name, the dock and\r\n     * the grid stay readable over the artwork. One small bitmap stretched over the screen, like the glow.\r\n     */\r\n    private Drawable scrimDrawable() {\r\n        int w = 480, h = 270;\r\n        Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);\r\n        Canvas canvas = new Canvas(bmp);\r\n        boolean rtl = getResources().getBoolean(R.bool.rtl_menu_bar);\r\n        Paint paint = new Paint();\r\n        paint.setShader(new LinearGradient(rtl ? w : 0, 0, rtl ? 0 : w, 0,\r\n                new int[]{0xD1060406, 0x59060406, 0x00060406}, new float[]{0f, 0.42f, 0.7f}, Shader.TileMode.CLAMP));\r\n        canvas.drawRect(0, 0, w, h, paint);\r\n        paint.setShader(new LinearGradient(0, 0, 0, h,\r\n                new int[]{0x00060406, 0x00060406, 0xDB060406, 0xFF060406}, new float[]{0f, 0.28f, 0.58f, 0.76f},\r\n                Shader.TileMode.CLAMP));\r\n        canvas.drawRect(0, 0, w, h, paint);\r\n        BitmapDrawable scrim = new BitmapDrawable(getResources(), bmp);\r\n        scrim.setGravity(Gravity.FILL);\r\n        return scrim;\r\n    }\r\n\r\n    /** Swaps the backdrop to this app's artwork: fade out, swap, fade in, the way the shelf's name changes. */\r\n    private void showHeroArt(int position) {\r\n        String key = adapter.keyAt(position);\r\n        if (key == null) return;\r\n        heroArt.animate().cancel();\r\n        if (key.equals(heroArtKey)) {\r\n            fadeHeroArt(420);\r\n            return;\r\n        }\r\n        Bitmap art = heroArtCache.get(key);\r\n        if (art == null) {\r\n            art = HeroArt.from(adapter.artworkAt(position));\r\n            if (art != null) heroArtCache.put(key, art);\r\n        }\r\n        heroArtKey = key;\r\n        final Bitmap next = art;\r\n        if (!animations || reducedMotion || heroArt.getAlpha() == 0f) {\r\n            heroArt.setImageBitmap(next);\r\n            fadeHeroArt(600);\r\n            return;\r\n        }\r\n        heroArt.animate().alpha(0f).setDuration(140).withEndAction(() -> {\r\n            heroArt.setImageBitmap(next);\r\n            heroArt.animate().alpha(heroArtAlpha).setDuration(420).start();\r\n        }).start();\r\n    }\r\n\r\n    private void fadeHeroArt(int ms) {\r\n        if (!animations) heroArt.setAlpha(heroArtAlpha);\r\n        else heroArt.animate().alpha(heroArtAlpha).setDuration(reducedMotion ? Math.min(ms, Motion.REDUCED_MS) : ms).start();\r\n    }\r\n\r\n    /** \"Last opened yesterday, 9:40 PM\", the time in white; nothing (but the same height) when it was never seen. */\r\n    private void updateHeroMeta(int position) {\r\n        long when = LastOpened.get(this, adapter.packageAt(position));\r\n        if (when <= 0) {\r\n            heroMeta.setVisibility(View.INVISIBLE);\r\n            return;\r\n        }\r\n        String rel = LastOpened.describe(this, when);\r\n        String text = getString(R.string.last_opened, rel);\r\n        SpannableString line = new SpannableString(text);\r\n        int at = text.indexOf(rel);\r\n        if (at >= 0) line.setSpan(new ForegroundColorSpan(0xFFFFFFFF), at, at + rel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\r\n        heroMeta.setText(line);\r\n        heroMeta.setVisibility(View.VISIBLE);\r\n    }\r\n\r\n    /** One dot per dock app, the focused one drawn long and white. Hebrew docks start on the right, so do the dots. */\r\n    private void updateHeroDots(int position) {\r\n        if (!heroDotsShown) return;\r\n        int count = Math.min(adapter.dockFilled(), COLUMNS);\r\n        heroDots.setVisibility(count > 1 ? View.VISIBLE : View.INVISIBLE);\r\n        if (heroDots.getChildCount() != count) {\r\n            heroDots.removeAllViews();\r\n            for (int i = 0; i < count; i++) {\r\n                View dot = new View(this);\r\n                GradientDrawable bg = new GradientDrawable();\r\n                bg.setCornerRadius(3 * density);\r\n                dot.setBackground(bg);\r\n                LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams((int) (6 * density), (int) (6 * density));\r\n                if (i > 0) lp.leftMargin = (int) (6 * density);\r\n                heroDots.addView(dot, lp);\r\n            }\r\n        }\r\n        for (int i = 0; i < count; i++) {\r\n            View dot = heroDots.getChildAt(rtlAppGrid ? count - 1 - i : i);\r\n            boolean on = i == position;\r\n            ((GradientDrawable) dot.getBackground()).setColor(on ? 0xFFFFFFFF : 0x4DFFFFFF);\r\n            ViewGroup.LayoutParams lp = dot.getLayoutParams();\r\n            int w = (int) ((on ? 20 : 6) * density);\r\n            if (lp.width != w) {\r\n                lp.width = w;\r\n                dot.setLayoutParams(lp);\r\n            }\r\n        }\r\n    }\r\n\r\n    /**\r\n     * The glow used to be a full-screen radial GradientDrawable. On Android 10 that drawable is\r\n     * drawn on the CPU, so fading it for half a second redrew 1920×1080 every frame and the\r\n     * highlight looked like it was running at a few frames per second. A small bitmap stretched\r\n     * over the screen is one texture. It is painted once in white and tinted with the app's colour,\r\n     * so changing apps neither repaints it on the CPU (about 11 ms) nor uploads it again.\r\n     */\r\n    private void setAmbient(int color) {\r\n        if (color == ambientColor && ambient.getBackground() != null) return;\r\n        ambientColor = color;\r\n        BitmapDrawable glow = (BitmapDrawable) ambient.getBackground();\r\n        if (glow == null) {\r\n            int w = 480;\r\n            int h = 270;\r\n            Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);\r\n            Canvas canvas = new Canvas(bmp);"}}