Cherish: Implement cutout force full screen [2/2]

Inspired by MIUI and Essential

Squashed commits:

 commit e483acceb29cdd70bd649a732a03780dacf73a6c
 Author: LuK1337 <priv.luk@gmail.com>
 Date:   Sat Sep 29 01:02:32 2018 +0200

    LineageParts: Remove dividers from expanded desktop/long screen apps list

    Change-Id: I699f7c20d8313ad779165db336aed7bc72e68d28

 commit f9304f0cdc0e92dac787e6d414d36de51cdbed85
 Author: Tony Mantler <nicoya@google.com>
 Date:   Thu Sep 21 15:50:27 2017 -0700

    LineageParts: Make ApplicationsState.Session lifecycle-aware

    Bug: 66682989
    Test: RunSettingsRoboTests
    Change-Id: Idf56788fcae22639b7bf604a8c73cd5f0ff2da16

 commit 1b921f454a4d4c30317b72958251e83ff3792d81
 Author: LuK1337 <priv.luk@gmail.com>
 Date:   Mon Jun 4 16:01:00 2018 +0200

    LineageParts: Add an option to force pre-O apps to use full screen aspect ratio

    When an app target pre-O releases, the default max aspect ratio
    is 1.86:1 which leads to ugly black areas on devices that have
    screens with higher aspect ratio (for example Galaxy S8/S9).

    This change adds an option to allow users to change aspect ratio
    for pre-O apps to full screen aspect ratio.

    Change-Id: I20eeabf140c9d278d21d68675aed856d45b2f78a

Change-Id: I49f150df9d07f25794f4af4d43e560591fb9596d
Signed-off-by: Pranav Vashi <neobuddy89@gmail.com>
Signed-off-by: Hưng Phan <phandinhhungvp2001@gmail.com>
This commit is contained in:
jhenrique09
2020-10-03 20:05:46 +00:00
committed by Hưng Phan
parent 91127d47a7
commit 4537e0bb8c
6 changed files with 526 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 The LineageOS Project
Copyright (C) 2019 The PixelExperience 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.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/cutout_force_fullscreen_prefs"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="@+id/user_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null" />
</RelativeLayout>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 The LineageOS Project
Copyright (C) 2019 The PixelExperience 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<ImageView
android:id="@+id/app_icon"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:layout_marginEnd="8dp"
android:scaleType="centerInside"
android:contentDescription="@null" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textColor="?android:attr/textColorPrimary"
android:textAppearance="@style/TextAppearance.Medium"
android:singleLine="true"
android:ellipsize="marquee" />
</LinearLayout>
<Switch
android:id="@+id/state"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -700,5 +700,9 @@
<!-- Fingerprint Ripple Effect -->
<string name="enable_fingerprint_ripple_effect_title">Ripple effect</string>
<string name="enable_fingerprint_ripple_effect_summary">Show ripple effect on unlock with fingerprint</string>
<!-- Notch: Full screen apps -->
<string name="display_cutout_force_fullscreen_title">Full screen apps</string>
<string name="display_cutout_force_fullscreen_summary">Force apps to ignore notch space</string>
</resources>

View File

@@ -30,6 +30,16 @@
android:summary="@string/charging_animation_summary"
android:defaultValue="false" />
<!-- Display Cutout -->
<Preference
android:key="display_cutout_force_fullscreen_settings"
android:title="@string/display_cutout_force_fullscreen_title"
android:summary="@string/display_cutout_force_fullscreen_summary"
android:fragment="com.cherish.settings.fragments.DisplayCutoutForceFullscreenSettings"
settings:controller="com.cherish.settings.fragments.DisplayCutoutForceFullscreenPreferenceController"
app:allowDividerAbove="true" />
<com.cherish.settings.preferences.SystemSettingSwitchPreference
android:key="use_photos_spoof"
android:title="@string/use_photos_spoof_title"

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2019 The PixelExperience 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.cherish.settings.fragments;
import android.content.Context;
import android.provider.Settings;
import android.os.Handler;
import android.os.UserHandle;
import com.android.internal.util.cherish.cutout.CutoutFullscreenController;
import com.android.settings.core.BasePreferenceController;
public class DisplayCutoutForceFullscreenPreferenceController extends BasePreferenceController {
private static final String PREF_KEY = "display_cutout_force_fullscreen_settings";
private CutoutFullscreenController mCutoutForceFullscreenSettings;
public DisplayCutoutForceFullscreenPreferenceController(Context context) {
super(context, PREF_KEY);
mCutoutForceFullscreenSettings = new CutoutFullscreenController(new Handler(), context);
}
@Override
public int getAvailabilityStatus() {
return mCutoutForceFullscreenSettings.isSupported() ?
AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
}

View File

@@ -0,0 +1,376 @@
/*
* Copyright (C) 2018 The LineageOS Project
* Copyright (C) 2019 PixelExperience
*
* 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.cherish.settings.fragments;
import android.app.ActivityManager;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SectionIndexer;
import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.cherish.cutout.CutoutFullscreenController;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settingslib.applications.ApplicationsState;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DisplayCutoutForceFullscreenSettings extends SettingsPreferenceFragment
implements ApplicationsState.Callbacks {
private ActivityManager mActivityManager;
private AllPackagesAdapter mAllPackagesAdapter;
private ApplicationsState mApplicationsState;
private ApplicationsState.Session mSession;
private ActivityFilter mActivityFilter;
private Map<String, ApplicationsState.AppEntry> mEntryMap =
new HashMap<String, ApplicationsState.AppEntry>();
private ListView mUserListView;
private CutoutFullscreenController mCutoutForceFullscreenSettings;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
mSession = mApplicationsState.newSession(this);
mSession.onResume();
mActivityManager = (ActivityManager) getActivity().getSystemService(
Context.ACTIVITY_SERVICE);
mActivityFilter = new ActivityFilter(getActivity().getPackageManager());
mAllPackagesAdapter = new AllPackagesAdapter(getActivity());
mCutoutForceFullscreenSettings = new CutoutFullscreenController(new Handler(), getContext());
mCutoutForceFullscreenSettings.registerObserver();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
getActivity().getActionBar().setTitle(R.string.display_cutout_force_fullscreen_title);
return inflater.inflate(R.layout.cutout_force_fullscreen_layout, container, false);
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
@Override
public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mUserListView = (ListView) view.findViewById(R.id.user_list_view);
mUserListView.setAdapter(mAllPackagesAdapter);
}
@Override
public void onResume() {
super.onResume();
rebuild();
}
@Override
public void onDestroy() {
super.onDestroy();
mSession.onPause();
mSession.onDestroy();
}
@Override
public void onPackageListChanged() {
mActivityFilter.updateLauncherInfoList();
rebuild();
}
@Override
public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> entries) {
if (entries != null) {
handleAppEntries(entries);
mAllPackagesAdapter.notifyDataSetChanged();
}
}
@Override
public void onLoadEntriesCompleted() {
rebuild();
}
@Override
public void onAllSizesComputed() {}
@Override
public void onLauncherInfoChanged() {}
@Override
public void onPackageIconChanged() {}
@Override
public void onPackageSizeChanged(String packageName) {}
@Override
public void onRunningStateChanged(boolean running) {}
private void handleAppEntries(List<ApplicationsState.AppEntry> entries) {
final ArrayList<String> sections = new ArrayList<String>();
final ArrayList<Integer> positions = new ArrayList<Integer>();
final PackageManager pm = getPackageManager();
String lastSectionIndex = null;
int offset = 0;
for (int i = 0; i < entries.size(); i++) {
final ApplicationInfo info = entries.get(i).info;
final String label = (String) info.loadLabel(pm);
final String sectionIndex;
if (!info.enabled) {
sectionIndex = "--"; // XXX
} else if (TextUtils.isEmpty(label)) {
sectionIndex = "";
} else {
sectionIndex = label.substring(0, 1).toUpperCase();
}
if (lastSectionIndex == null ||
!TextUtils.equals(sectionIndex, lastSectionIndex)) {
sections.add(sectionIndex);
positions.add(offset);
lastSectionIndex = sectionIndex;
}
offset++;
}
mAllPackagesAdapter.setEntries(entries, sections, positions);
mEntryMap.clear();
for (ApplicationsState.AppEntry e : entries) {
mEntryMap.put(e.info.packageName, e);
}
}
private void rebuild() {
mSession.rebuild(mActivityFilter, ApplicationsState.ALPHA_COMPARATOR);
}
private class AllPackagesAdapter extends BaseAdapter
implements SectionIndexer {
private final LayoutInflater mInflater;
private List<ApplicationsState.AppEntry> mEntries = new ArrayList<>();
private String[] mSections;
private int[] mPositions;
public AllPackagesAdapter(Context context) {
mInflater = LayoutInflater.from(context);
mActivityFilter = new ActivityFilter(context.getPackageManager());
}
@Override
public int getCount() {
return mEntries.size();
}
@Override
public Object getItem(int position) {
return mEntries.get(position);
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public long getItemId(int position) {
return mEntries.get(position).id;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ApplicationsState.AppEntry entry = mEntries.get(position);
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder(mInflater.inflate(
R.layout.cutout_force_fullscreen_list_item, parent, false));
holder.state.setOnCheckedChangeListener((buttonView, isChecked) -> {
final ApplicationsState.AppEntry appEntry =
(ApplicationsState.AppEntry) buttonView.getTag();
if (isChecked) {
mCutoutForceFullscreenSettings.addApp(appEntry.info.packageName);
} else {
mCutoutForceFullscreenSettings.removeApp(appEntry.info.packageName);
}
try{
mActivityManager.forceStopPackage(appEntry.info.packageName);
}catch(Exception ignored){
}
});
} else {
holder = (ViewHolder) convertView.getTag();
}
if (entry == null) {
return holder.rootView;
}
holder.title.setText(entry.label);
mApplicationsState.ensureIcon(entry);
holder.icon.setImageDrawable(entry.icon);
holder.state.setTag(entry);
holder.state.setChecked(mCutoutForceFullscreenSettings.shouldForceCutoutFullscreen(entry.info.packageName));
return holder.rootView;
}
private void setEntries(List<ApplicationsState.AppEntry> entries,
List<String> sections, List<Integer> positions) {
mEntries = entries;
mSections = sections.toArray(new String[sections.size()]);
mPositions = new int[positions.size()];
for (int i = 0; i < positions.size(); i++) {
mPositions[i] = positions.get(i);
}
notifyDataSetChanged();
}
@Override
public int getPositionForSection(int section) {
if (section < 0 || section >= mSections.length) {
return -1;
}
return mPositions[section];
}
@Override
public int getSectionForPosition(int position) {
if (position < 0 || position >= getCount()) {
return -1;
}
final int index = Arrays.binarySearch(mPositions, position);
/*
* Consider this example: section positions are 0, 3, 5; the supplied
* position is 4. The section corresponding to position 4 starts at
* position 3, so the expected return value is 1. Binary search will not
* find 4 in the array and thus will return -insertPosition-1, i.e. -3.
* To get from that number to the expected value of 1 we need to negate
* and subtract 2.
*/
return index >= 0 ? index : -index - 2;
}
@Override
public Object[] getSections() {
return mSections;
}
}
private static class ViewHolder {
private TextView title;
private ImageView icon;
private Switch state;
private View rootView;
private ViewHolder(View view) {
this.title = (TextView) view.findViewById(R.id.app_name);
this.icon = (ImageView) view.findViewById(R.id.app_icon);
this.state = (Switch) view.findViewById(R.id.state);
this.rootView = view;
view.setTag(this);
}
}
private class ActivityFilter implements ApplicationsState.AppFilter {
private final PackageManager mPackageManager;
private final List<String> mLauncherResolveInfoList = new ArrayList<String>();
private ActivityFilter(PackageManager packageManager) {
this.mPackageManager = packageManager;
updateLauncherInfoList();
}
public void updateLauncherInfoList() {
Intent i = new Intent(Intent.ACTION_MAIN);
i.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfoList = mPackageManager.queryIntentActivities(i, 0);
synchronized (mLauncherResolveInfoList) {
mLauncherResolveInfoList.clear();
for (ResolveInfo ri : resolveInfoList) {
mLauncherResolveInfoList.add(ri.activityInfo.packageName);
}
}
}
@Override
public void init() {}
private final String[] hideApps = {"com.android.settings", "com.android.documentsui",
"com.android.fmradio", "com.caf.fmradio", "com.android.stk",
"com.google.android.calculator", "com.google.android.calendar",
"com.google.android.deskclock", "com.google.android.contacts",
"com.google.android.apps.messaging", "com.google.android.googlequicksearchbox",
"com.android.vending", "com.google.android.dialer",
"com.google.android.apps.wallpaper", "com.google.android.as"};
@Override
public boolean filterApp(ApplicationsState.AppEntry entry) {
boolean show = !mAllPackagesAdapter.mEntries.contains(entry.info.packageName);
if (show) {
synchronized (mLauncherResolveInfoList) {
show = mLauncherResolveInfoList.contains(entry.info.packageName) && !Arrays.asList(hideApps).contains(entry.info.packageName);
}
}
return show;
}
}
@Override
public int getMetricsCategory() {
return MetricsEvent.CHERISH_SETTINGS;
}
}