blob: bf13017d318cc22507947b6ab92110c53d731493 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.car.kitchensink.rotary;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.google.android.car.kitchensink.R;
import java.util.HashSet;
import java.util.Set;
/** Test fragment to enable/disable various components related to RotaryController. */
public final class RotaryFragment extends Fragment {
private static final String TAG = RotaryFragment.class.getSimpleName();
private static final String ROTARY_CONTROLLER_PACKAGE = "com.android.car.rotary";
private static final ComponentName ROTARY_SERVICE_NAME = ComponentName.unflattenFromString(
ROTARY_CONTROLLER_PACKAGE + "/com.android.car.rotary.RotaryService");
private static final String ROTARY_PLAYGROUND_PACKAGE = "com.android.car.rotaryplayground";
private static final String ROTARY_IME_PACKAGE = "com.android.car.rotaryime";
private static final String ACCESSIBILITY_DELIMITER = ":";
private static final int ON = 1;
private static final int OFF = 0;
private final IntentFilter mFilter = new IntentFilter();
private TextView mUnavailableMessage;
private Button mEnableAllButton;
private Button mDisableAllButton;
private Switch mRotaryServiceToggle;
private Switch mRotaryImeToggle;
private Switch mRotaryPlaygroundToggle;
private final BroadcastReceiver mPackagesUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case Intent.ACTION_PACKAGE_ADDED:
case Intent.ACTION_PACKAGE_CHANGED:
case Intent.ACTION_PACKAGE_REMOVED:
refreshUi();
break;
}
}
};
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
mFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
mFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.rotary_fragment, container, /* attachToRoot= */ false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mUnavailableMessage = view.findViewById(R.id.unavailable_message);
mEnableAllButton = view.findViewById(R.id.enable_all_rotary);
mDisableAllButton = view.findViewById(R.id.disable_all_rotary);
mRotaryServiceToggle = view.findViewById(R.id.rotary_service_toggle);
mRotaryImeToggle = view.findViewById(R.id.rotary_ime_toggle);
mRotaryPlaygroundToggle = view.findViewById(R.id.rotary_playground_toggle);
mRotaryServiceToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> enableRotaryService(getContext(), isChecked));
mRotaryImeToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> enableApplication(getContext(), ROTARY_IME_PACKAGE,
isChecked));
mRotaryPlaygroundToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> enableApplication(getContext(),
ROTARY_PLAYGROUND_PACKAGE, isChecked));
mEnableAllButton.setOnClickListener(v -> {
if (mRotaryServiceToggle.isEnabled()) {
mRotaryServiceToggle.setChecked(true);
}
if (mRotaryImeToggle.isEnabled()) {
mRotaryImeToggle.setChecked(true);
}
if (mRotaryPlaygroundToggle.isEnabled()) {
mRotaryPlaygroundToggle.setChecked(true);
}
});
mDisableAllButton.setOnClickListener(v -> {
if (mRotaryServiceToggle.isEnabled()) {
mRotaryServiceToggle.setChecked(false);
}
if (mRotaryImeToggle.isEnabled()) {
mRotaryImeToggle.setChecked(false);
}
if (mRotaryPlaygroundToggle.isEnabled()) {
mRotaryPlaygroundToggle.setChecked(false);
}
});
}
@Override
public void onStart() {
super.onStart();
getContext().registerReceiver(mPackagesUpdatedReceiver, mFilter);
}
@Override
public void onResume() {
super.onResume();
refreshUi();
}
private void refreshUi() {
ApplicationInfo info = findApplication(getContext(), ROTARY_CONTROLLER_PACKAGE);
boolean rotaryApplicationExists = info != null;
info = findApplication(getContext(), ROTARY_IME_PACKAGE);
boolean rotaryImeExists = info != null;
info = findApplication(getContext(), ROTARY_PLAYGROUND_PACKAGE);
boolean rotaryPlaygroundExists = info != null;
mUnavailableMessage.setVisibility(!rotaryApplicationExists ? View.VISIBLE : View.GONE);
mEnableAllButton.setEnabled(rotaryApplicationExists);
mDisableAllButton.setEnabled(rotaryApplicationExists);
mRotaryServiceToggle.setEnabled(rotaryApplicationExists);
mRotaryImeToggle.setEnabled(rotaryApplicationExists && rotaryImeExists);
mRotaryPlaygroundToggle.setEnabled(rotaryApplicationExists && rotaryPlaygroundExists);
mRotaryServiceToggle.setChecked(isRotaryServiceEnabled(getContext()));
mRotaryImeToggle.setChecked(
rotaryImeExists && isApplicationEnabled(getContext(), ROTARY_IME_PACKAGE));
mRotaryPlaygroundToggle.setChecked(
rotaryPlaygroundExists && isApplicationEnabled(getContext(),
ROTARY_PLAYGROUND_PACKAGE));
}
@Override
public void onStop() {
super.onStop();
getContext().unregisterReceiver(mPackagesUpdatedReceiver);
}
@Nullable
private static ApplicationInfo findApplication(Context context, String packageName) {
PackageManager pm = context.getPackageManager();
try {
Log.d(TAG, "Searching for: " + packageName);
return pm.getApplicationInfoAsUser(packageName, /* flags= */ 0,
ActivityManager.getCurrentUser());
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Could not find: " + packageName);
return null;
}
}
private static boolean isApplicationEnabled(Context context, String packageName) {
ApplicationInfo info = findApplication(context, packageName);
if (info == null) {
return false;
}
return info.enabled;
}
private static void enableApplication(Context context, String packageName, boolean enable) {
// Check that the application exists.
ApplicationInfo info = findApplication(context, packageName);
if (info == null) {
Log.e(TAG, "Cannot enable application. " + packageName + " package does not exist");
return;
}
PackageManager pm = context.getPackageManager();
int currentState = pm.getApplicationEnabledSetting(packageName);
int desiredState = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
boolean isEnabled = currentState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
if (isEnabled != enable
|| currentState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
Log.d(TAG, "Application state updated for " + packageName + ": " + enable);
pm.setApplicationEnabledSetting(packageName, desiredState, /* flags= */ 0);
}
}
private static boolean isRotaryServiceEnabled(Context context) {
ApplicationInfo info = findApplication(context, ROTARY_CONTROLLER_PACKAGE);
if (info == null) {
return false;
}
int isAccessibilityEnabled = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED, OFF);
if (isAccessibilityEnabled != ON) {
return false;
}
String services = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (services == null) {
return false;
}
return services.contains(ROTARY_CONTROLLER_PACKAGE);
}
private static void enableRotaryService(Context context, boolean enable) {
// Check that the RotaryController exists.
ApplicationInfo info = findApplication(context, ROTARY_CONTROLLER_PACKAGE);
if (info == null) {
Log.e(TAG, "Cannot enable rotary service. " + ROTARY_CONTROLLER_PACKAGE
+ " package does not exist");
return;
}
// Set the list of accessibility services.
String services = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (services == null) {
services = "";
}
Log.d(TAG, "Current list of accessibility services: " + services);
String[] servicesArray = services.split(ACCESSIBILITY_DELIMITER);
Set<ComponentName> servicesSet = new HashSet<>();
for (String service : servicesArray) {
ComponentName name = ComponentName.unflattenFromString(service);
if (name != null) {
servicesSet.add(name);
}
}
if (enable) {
servicesSet.add(ROTARY_SERVICE_NAME);
} else {
servicesSet.remove(ROTARY_SERVICE_NAME);
}
StringBuilder builder = new StringBuilder();
for (ComponentName service : servicesSet) {
if (builder.length() > 0) {
builder.append(ACCESSIBILITY_DELIMITER);
}
builder.append(service.flattenToString());
}
Log.d(TAG, "New list of accessibility services: " + builder);
Settings.Secure.putString(context.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
builder.toString());
// Set the overall enabled state.
int desiredState = enable ? ON : OFF;
Settings.Secure.putInt(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED,
desiredState);
}
}