mardi 24 février 2015

[dev] Restore Sillent Mode for Lollipop with Xposed topic






I'm looking for help in development, not general chitchat. If you don't know anything about coding or have anything new to add to this thread please do not post at all! Thanks for your cooperation!


Hello alltogether

I digged into the Lollipop code to look for a solution to restore the old Kitkat silent mode in Lollipop with Xposed.

I stumbled about something interesting:
Github: VolumeZen: combine ringer/notification volume and zen.
Google explicitly mentioned the silent mode here:

Quote:









- Don't allow volume changes to set ringer mode silent.




They introduced a new variable:

Code:


/** Allow volume changes to set ringer mode to silent? */
private static final boolean VOLUME_SETS_RINGER_MODE_SILENT = false;


There are only 3 results regarding this on Google, so I guess I'm the first to find it :D

My first attempt thus was to change this with Xposed. Unfortunately the variable is final and gets inclinded so you can't just hook and replace the ressource with Xposed. So I'm attempting to replace the methods in question.

Here is my current work-in-progress code.
It does seem to work to some extent, after going to the vibrate mode another Vol- press does... something. Unfortunately it activates the priority mode (and breaks the interface a bit) rather than the silent mode.




Code:


import android.media.AudioManager;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;


public class TrueSilentMode implements IXposedHookLoadPackage {
    private static final String TAG = "TrueSilentMoe";
    private static final boolean DEBUG = true;
    public int mPrevVolDirection;

    private static void log(String message) {
        XposedBridge.log(TAG + ": " + message);
    }


    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        if (lpparam.packageName != "android")
            return;

        final Class<?> classAudioService = XposedHelpers.findClass("android.media.AudioService", lpparam.classLoader);

        XposedHelpers.findAndHookMethod(classAudioService, "onSetStreamVolume",
                int.class, int.class, int.class, int.class, new XC_MethodReplacement() {

                    @Override
                    protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                        Integer streamType = (Integer) param.args[0];
                        Integer index = (Integer) param.args[1];
                        Integer flags = (Integer) param.args[2];
                        Integer device = (Integer) param.args[3];
                        boolean mHasVibrator = XposedHelpers.getBooleanField(param.thisObject, "mHasVibrator");
                        if (DEBUG) log("hasVibrator: " + mHasVibrator);

                        int[] mStreamVolumeAlias = (int[]) XposedHelpers.getObjectField(param.thisObject, "mStreamVolumeAlias");
                        int masterStreamType = (int) XposedHelpers.callMethod(param.thisObject, "getMasterStreamType");

                        XposedHelpers.callMethod(
                                param.thisObject, "setStreamVolumeInt",
                                mStreamVolumeAlias[streamType], index, device, false);

                        if (DEBUG) log("mstreamarray: " + mStreamVolumeAlias[streamType]);

                        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
                                (mStreamVolumeAlias[streamType] == masterStreamType)) {
                            int newRingerMode;
                            if (index == 0) {
                                // or overwrite with newRingerMode = AudioManager.RINGER_MODE_SILENT; ?
                                newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE : AudioManager.RINGER_MODE_SILENT;
                            } else {
                                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
                            }
                            log("setringermode : " + newRingerMode);
                            XposedHelpers.callMethod(param.thisObject, "setRingerMode", newRingerMode, false);
                        }

                        //TODO null?
                        return null;
                    }
                });

        XposedHelpers.findAndHookMethod(classAudioService, "checkForRingerModeChange",
                int.class, int.class, int.class, new XC_MethodReplacement() {

                    @Override
                    protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
                        int oldIndex = (Integer) param.args[0];
                        int direction = (Integer) param.args[1];
                        int step = (Integer) param.args[2];

                        int ringerMode = (int) XposedHelpers.callMethod(param.thisObject, "getRingerMode");
                        boolean mHasVibrator = XposedHelpers.getBooleanField(param.thisObject, "mHasVibrator");

                        //int result = XposedHelpers.getIntField(param.thisObject, "FLAG_ADJUST_VOLUME");
                        final int FLAG_ADJUST_VOLUME = 1;
                        int result = FLAG_ADJUST_VOLUME;
                        int FLAG_SHOW_SILENT_HINT = 1 << 7; //copied from android.media.Audiosystem

                        switch (ringerMode) {
                            case RINGER_MODE_NORMAL:
                                if (direction == AudioManager.ADJUST_LOWER) {
                                    if (mHasVibrator) {
                                        // "step" is the delta in internal index units corresponding to a
                                        // change of 1 in UI index units.
                                        // Because of rounding when rescaling from one stream index range to its alias
                                        // index range, we cannot simply test oldIndex == step:
                                        //  (step <= oldIndex < 2 * step) is equivalent to: (old UI index == 1)
                                        if (step <= oldIndex && oldIndex < 2 * step) {
                                            ringerMode = RINGER_MODE_VIBRATE;
                                        }
                                    } else {
                                        // (oldIndex < step) is equivalent to (old UI index == 0)
                                        if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
                                            ringerMode = RINGER_MODE_SILENT;
                                        }
                                    }
                                }
                                break;
                            case RINGER_MODE_VIBRATE:
                                if (!mHasVibrator) {
                                    log("checkForRingerModeChange() current ringer mode is vibrate but no vibrator is present");
                                    break;
                                }
                                if ((direction == AudioManager.ADJUST_LOWER)) {
                                    if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
                                        if (DEBUG) log("ringMode = RINGER_MODE_SILENT");
                                        ringerMode = RINGER_MODE_SILENT;
                                    }
                                } else if (direction == AudioManager.ADJUST_RAISE) {
                                    ringerMode = RINGER_MODE_NORMAL;
                                }
                                result &= ~FLAG_ADJUST_VOLUME;
                                break;
                            case RINGER_MODE_SILENT:
                                if (direction == AudioManager.ADJUST_RAISE) {
                                    if (XposedHelpers.getBooleanField(param.thisObject, "PREVENT_VOLUME_ADJUSTMENT_IF_SILENT")) {
                                        result |= FLAG_SHOW_SILENT_HINT; //AudioManager.FLAG_SHOW_SILENT_HINT doesn't resolve for some reason
                                    } else {
                                        if (mHasVibrator) {
                                            ringerMode = RINGER_MODE_VIBRATE;
                                        } else {
                                            ringerMode = RINGER_MODE_NORMAL;
                                        }
                                    }
                                }
                                result &= ~FLAG_ADJUST_VOLUME;
                                break;
                            default:
                                log("checkForRingerModeChange() wrong ringer mode: " + ringerMode);
                                break;
                        }
                        XposedHelpers.callMethod(param.thisObject, "setRingerMode", ringerMode, false);
                        mPrevVolDirection = direction;

                        return result;
                    }
                });

        }
}






Now I'm wondering if there's a problem in my code - this is the first time I'm using Xposed, just studied the documentation a couple hours ago, so it is quite possible.

If not.. then my attempt does seem to be wrong. By now I've tried a couple other things like hooking the ZenModeHelper, messing with the vibration status and a couple others, but so far this didn't lead me anywhere.

The AudioManager code is quite complex, maybe I'm missing the needle in a haystackand someone can tell me a better place to archive what I'm intending?


Some additional information regarding silent mode:
  • No, the new "none" or "priority" modes are not substitutes.
    None: When you activate None you're alarms are turned off and your LED won't display anything.
    Priority: When you activate the priority mode without any priority interruptions you a) still won't have any LED notifications and b) totally loose on the real intention of the priority mode to actually let some notifications pass (like calls) - but not all.


  • There is a bug which you can use to actually activate the silent mode on the Nexus 5 (and possibly other devices):
    Bring your volume all the way down (= set it to vibrate only), then press VolUp once. Now reboot, voila you're in silent mode.
    Unfortunately you need to repear the reboot everytime you changed the notification volume.
    And funny incident, after my tests with Xposed this doesn't seem to work anymore, I will try with a full wipe again later on.


  • Afaik tablets do NOT have this restriction and can use the silent mode. Any confirmations? If yes, does this apply for both Wifi and LTE tablets?
    This actually is strange as I couldn't find any differences in the code for tablets - except maybe regarding the getMasterStreamType (that's why I'm asking about LTE/wifi)



Thanks!






1 commentaire:

  1. The greatest quality deer velvet extract has been acquired
    by your research and imported it.

    Here is my webpage - garcinia cambogia free trial australia (Pete)

    RépondreSupprimer