Im Zug - in einer ruhigen Bibliothek - in einem Konferenzraum - Als ich die App startete, hörte ich ein lautes Geräusch. Hast du so eine Erfahrung? Glücklicherweise habe ich es noch nicht erlebt, aber ich habe die Angewohnheit, die Lautstärke viele Male zu überprüfen.
Ich habe eine App erstellt, um ein solches Problem zu lösen.
Download über den unten stehenden Link
Es ist Open Source (MIT-Lizenz). Repository
Diese App setzt die Lautstärke des Lautsprechers (STREAM_MUSIC) auf Null, wenn der Kopfhörer nicht angeschlossen ist. Stellen Sie die Lautstärke zum folgenden Zeitpunkt auf Null.
Diese App befindet sich im Benachrichtigungsbereich (Vordergrunddienst) und versucht immer, die Lautsprecherlautstärke auf Null zu reduzieren. Es gibt jedoch Situationen, in denen Sie einen Lautsprecher verwenden möchten. Daher haben wir eine Funktion implementiert, um den Stummschaltungsvorgang vorübergehend abzubrechen.
Wenn nur das oben Genannte getan wird, wird es ein Werbeartikel sein, also werde ich den Implementierungsinhalt schreiben.
Dies ist der Kern dieser App, die die Lautstärke des Lautsprechers auf Null reduziert. Sie kann in der Klasse AudioManager festgelegt werden.
AudioManager audioManager = this.getSystemService(this, AudioManager.class);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
Holen Sie sich einfach den "AudioManager" und rufen Sie die "setStreamVolume" -Methode auf. Unter Android sind die Lautstärkeeinstellungen für jeden Stream getrennt, und jede Lautstärke kann unabhängig eingestellt werden. Geben Sie "STREAM_MUSIC" an, um nur die Musik in dieser App auf Null zu setzen.
Es sieht auch so aus, als ob Sie die Berechtigung "android.permission.MODIFY_AUDIO_SETTINGS" benötigen, dies war jedoch nicht der Fall.
Diese App setzt die Lautstärke nicht auf Null, wenn die Kopfhörer angeschlossen sind. Daher müssen Sie den Verbindungsstatus der Kopfhörer überprüfen. Sie können den Verbindungsstatus mit "AudioManager" überprüfen, aber der Beurteilungscode ändert sich je nach Android-Version. Android vor Android 6.0 M verwendet die Methoden "isWiredHeadsetOn", "isBluetoothScoOn" und "isBluetoothA2dpOn", und Android 6.0 M verwendet die Methode "getDevices".
private boolean isHeadsetConnected() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return this.audioManager.isWiredHeadsetOn() || this.audioManager.isBluetoothScoOn() || this.audioManager.isBluetoothA2dpOn();
} else {
AudioDeviceInfo[] devices = this.audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
for (int i = 0; i < devices.length; i++) {
AudioDeviceInfo device = devices[i];
int type = device.getType();
if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET
|| type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES
|| type == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
|| type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO
) {
return true;
}
}
return false;
}
}
Selbst wenn die Lautstärke auf Null gesetzt ist, ist es bedeutungslos, wenn der Benutzer oder andere Apps die Lautstärke ändern. Daher benötigen wir eine Funktion, um das Volumen auf Null zu setzen, wenn das Volumen geändert wird. Unter Android Content Provider und ContentObserver Sie können die Lautstärkeänderung durch Registrierung erkennen.
public final class DNSContentObserver extends ContentObserver {
private Runnable callback;
public DNSContentObserver(Handler handler, Runnable callback) {
super(handler);
this.callback = callback;
}
@Override
public boolean deliverSelfNotifications() {
return false;
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
this.callback.run();
}
}
this.getContentResolver().registerContentObserver(
android.provider.Settings.System.getUriFor("volume_music_speaker"),
true,
new DNSContentObserver(new Handler(), new Runnable() {
@Override
public void run() {
//Stummschaltung
}
}));
Holen Sie sich die ContentResolver-Klasse mit getContentResolver und registrieren Sie den ContentObserver mit der registerContentObserver-Methode. Zu diesem Zeitpunkt können Sie mithilfe der als Inhalts-URI bezeichneten Zeichenfolge angeben, welche Daten überwacht werden sollen. In dieser App möchten wir Änderungen in der Lautstärke des Lautsprechers erkennen, daher geben wir "content: // settings / system / volume_music_speaker" an.
Wenn bei dem oben genannten Verfahren zur Erkennung von Volumenänderungen das Volumen in kurzer Zeit kontinuierlich geändert wird, tritt möglicherweise nicht "onChange" von "ContentObserver" auf (können mehrere Änderungen zu einem "onChange" kombiniert werden?). Um diesen Effekt abzuschwächen, schalten Sie 1 Sekunde nach dem Auftreten von "onChange" erneut stumm. Ich habe eine Klasse erstellt, um die [Debounce] -Verarbeitung (http://reactivex.io/documentation/operators/debounce.html) durchzuführen.
public final class Debouncer {
private static final String TAG = "Debouncer";
private final Handler handler;
private final int dueTime;
private final Runnable callback;
private final Runnable checkRunner = new Runnable() {
@Override
public void run() {
Debouncer.this.check();
}
};
private final AtomicBoolean locked = new AtomicBoolean(false);
private int elapsedTime;
private long startTime;
public Debouncer(Handler handler, int dueTime, Runnable callback) {
this.handler = handler;
this.dueTime = dueTime;
this.callback = callback;
this.elapsedTime = this.dueTime;
}
public void update() {
for (; ; ) {
// lock
if (this.locked.compareAndSet(false, true)) {
int elapsed = this.elapsedTime;
// reset time
Log.d(TAG, "reset");
this.startTime = System.currentTimeMillis();
this.elapsedTime = 0;
// not running?
if (elapsed >= this.dueTime) {
this.start(this.dueTime);
}
// unlock
this.locked.set(false);
break;
}
}
}
private void start(int delayTime) {
Log.d(TAG, "start");
this.handler.postDelayed(this.checkRunner, delayTime);
}
private void check() {
Log.d(TAG, "check");
// lock
if (this.locked.compareAndSet(false, true)) {
long currentTime = System.currentTimeMillis();
this.elapsedTime += currentTime - this.startTime;
boolean over = this.elapsedTime >= this.dueTime;
// retry
if (!over) {
int remainTime = this.dueTime - this.elapsedTime;
this.startTime = currentTime;
this.start(remainTime);
}
// unlock
this.locked.set(false);
// callback
if (over) this.callback.run();
}
}
}
Unter Android wird "android.media.AUDIO_BECOMING_NOISY" gesendet, sobald der Kopfhörer herausgezogen und der Ton über den Lautsprecher ausgegeben wird.
public final class DNSReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null) return;
switch (action) {
case AudioManager.ACTION_AUDIO_BECOMING_NOISY: {
//Stummschaltung
break;
}
}
}
}
Ich schreibe auch einen Absichtsfilter in das Manifest.
<intent-filter>
<action android:name="android.media.AUDIO_BECOMING_NOISY" />
</intent-filter>
Übrigens, bevor Sie den Verbindungsstatus des Kopfhörers in Android aktualisieren, wird diese Sendung aufgerufen. Sie sollten daher nicht prüfen, ob der Kopfhörer zu diesem Zeitpunkt angeschlossen ist. Diese App wird unabhängig vom Status der Kopfhörerverbindung zwangsweise stummgeschaltet.
Der Dienst wird beendet, wenn das Terminal neu gestartet oder die Anwendung aktualisiert wird. Wenn nichts unternommen wird, kann es zu einer Tragödie kommen. Daher muss die App automatisch gestartet werden. Empfangen Sie in einem solchen Fall die Sendung "android.intent.action.BOOT_COMPLETED" und die Sendung "android.intent.action.MY_PACKAGE_REPLACED" und starten Sie den Dienst.
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
Sie benötigen auch Berechtigungen.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Der Code ähnelt "AUDIO_BECOMING_NOISY" und wird daher weggelassen.
Unter Android können Sie Dienste nicht direkt vom Launcher aus starten. Sie können (wahrscheinlich) nur Aktivitäten über den Launcher starten. Diese App muss jedoch keine Aktivität anzeigen, daher habe ich sie so angepasst, dass keine Aktivität angezeigt wird.
Ändern Sie das AppTheme wie folgt:
<resources>
<style name="AppTheme" parent="android:Theme.Material">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
</style>
</resources>
Dies ist ein transparentes Thema für Aktivitäten ohne Titelleiste. Sie können den Dienst dann mit "onCreate" in "MainActivity" starten und sofort "finishAndRemoveTask" aufrufen, um die Aktivität vor dem Erscheinungsbild zu verbergen.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Dienst starten
this.finishAndRemoveTask();
this.overridePendingTransition(0, 0);
}
Es scheint auch, dass die Übergangsanimation durch Aufrufen von "overridePendingTransition" abgebrochen werden kann.
Als ich eine App in Android Studio erstellt habe, hat die APK-Größe 1 MB überschritten. Für residente Apps ist es definitiv besser, weniger Speicher zu verwenden. Daher haben wir verschiedene Reduzierungen vorgenommen, um die App-Größe zu reduzieren.
Setzen Sie zuerst "minifyEnabled" und "shirinkResources" in ProGuard auf "true". Allein dies machte es ungefähr 700KB.
release {
minifyEnabled true
shrinkResources true
...
}
Normalerweise glaube ich nicht, dass es ein Problem gibt, wenn es so klein wie das KB-Level ist, aber ich war nicht zufrieden und habe beschlossen, es kleiner zu machen. In dieser App hat die durch "Abhängigkeiten" angegebene Abhängigkeitsbibliothek die APK aufgeblasen.
build.gradle
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
implementation 'androidx.core:core-ktx:1.1.0-alpha03'
implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}
Dies ist Android X, eine Unterstützungsbibliothek zur Aufrechterhaltung der Abwärtskompatibilität mit Android, und ich denke, dass dies für allgemeine Apps fast unerlässlich ist. Sie können die APK jedoch verkleinern, indem Sie sie wegwerfen. (Nicht empfohlen.)
Ich konnte eine APK mit weniger als 100 KB erstellen, indem ich AndroidX entfernte und meinen eigenen abwärtskompatiblen Code schrieb. Die endgültige Größe beträgt ca. 26 KB. (Übrigens habe ich es zuerst in Kotlin geschrieben, aber ich habe es in Java geändert, weil der generierte Bytecode subtil war. Companion-Objekt ...)
Recommended Posts