Initial commit

This commit is contained in:
Peter Cai 2023-04-25 13:41:30 -04:00
commit 10a7f2f520
96 changed files with 5445 additions and 0 deletions

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
*.iml
.gradle
/local.properties
.idea
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
*.jks
*.keystore
keystore.properties

111
AndroidManifest.xml Normal file
View file

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015-2016 The CyanogenMod Project
2017-2018 The LineageOS Project
2020-2022 Paranoid Android
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.typeblog.lunatic"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="android.uid.system">
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-sdk
android:minSdkVersion="31"
android:targetSdkVersion="31"/>
<application
android:label="@string/spotlight_settings_app_name"
android:persistent="true"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true">
<receiver
android:name="net.typeblog.lunatic.BootCompletedReceiver"
android:exported="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<service
android:name="net.typeblog.lunatic.Services.CallReceiverService"
android:permission="android.permission.READ_PHONE_STATE">
</service>
<service
android:name="net.typeblog.lunatic.Services.ChargingService">
</service>
<service
android:name="net.typeblog.lunatic.Services.FlashlightService">
</service>
<service
android:name="net.typeblog.lunatic.Services.MusicService">
</service>
<service
android:name="net.typeblog.lunatic.Services.NotificationService"
android:exported="false"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<activity
android:name="net.typeblog.lunatic.Settings.SettingsActivity"
android:exported="true"
android:label="@string/spotlight_settings_title"
android:theme="@style/Theme.SubSettingsBase">
<!--<intent-filter>
<action android:name="com.android.settings.action.EXTRA_SETTINGS" />
</intent-filter>
<meta-data
android:name="com.android.settings.category"
android:value="com.android.settings.category.ia.homepage" />
<meta-data
android:name="com.android.settings.icon"
android:resource="@drawable/ic_spotlights_logo" />
<meta-data
android:name="com.android.settings.summary"
android:value="@string/spotlight_settings_summary" />
<meta-data
android:name="com.android.settings.order"
android:value="-105" />-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="net.typeblog.lunatic.Settings.NotifsSettingsActivity"
android:exported="false"
android:theme="@style/Theme.SubSettingsBase">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
</application>
</manifest>

1
SettingsLib/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2014 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
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib">
<uses-sdk android:minSdkVersion="29" />
</manifest>

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 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.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/content_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:outlineAmbientShadowColor="@android:color/transparent"
android:outlineSpotShadowColor="@android:color/transparent"
android:background="?android:attr/colorPrimary"
android:theme="@style/Theme.CollapsingToolbar.Settings">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/settingslib_toolbar_layout_height"
android:clipToPadding="false"
app:forceApplySystemWindowInsetTop="true"
app:extraMultilineHeightEnabled="true"
app:contentScrim="@color/settingslib_colorSurfaceHeader"
app:maxLines="3"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:scrimAnimationDuration="50"
app:scrimVisibleHeightTrigger="@dimen/settingslib_scrim_visible_height_trigger"
app:statusBarScrim="@null"
app:titleCollapseMode="fade"
app:collapsedTitleTextAppearance="@style/CollapsingToolbarTitle.Collapsed"
app:expandedTitleTextAppearance="@style/CollapsingToolbarTitle.Expanded"
app:expandedTitleMarginStart="@dimen/expanded_title_margin_start"
app:expandedTitleMarginEnd="@dimen/expanded_title_margin_end"
app:toolbarId="@id/action_bar">
<Toolbar
android:id="@+id/action_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="?android:attr/actionBarTheme"
android:transitionName="shared_element_view"
app:layout_collapseMode="pin"/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<!-- The main content view -->
<LinearLayout
android:id="@+id/content_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:transitionGroup="true"
android:orientation="vertical">
<Toolbar
android:id="@+id/action_bar"
style="?android:attr/actionBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?android:attr/actionBarTheme" />
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<resources>
<style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
<item name="elevationOverlayEnabled">true</item>
<item name="elevationOverlayColor">?attr/colorPrimary</item>
<item name="colorPrimary">@color/settingslib_primary_dark_device_default_settings</item>
<item name="colorAccent">@color/settingslib_accent_device_default_dark</item>
</style>
</resources>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<resources>
<!-- Collapsing toolbar layout dimensions -->
<dimen name="settingslib_toolbar_layout_height">179dp</dimen>
<dimen name="settingslib_scrim_visible_height_trigger">137dp</dimen>
<dimen name="expanded_title_margin_start">24dp</dimen>
<dimen name="expanded_title_margin_end">24dp</dimen>
</resources>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<resources>
<style name="CollapsingToolbarTitle.Collapsed" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
<item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
<item name="android:textSize">20dp</item>
<item name="android:textColor">@color/settingslib_text_color_primary_device_default</item>
</style>
<style name="CollapsingToolbarTitle.Expanded" parent="CollapsingToolbarTitle.Collapsed">
<item name="android:textSize">36dp</item>
<item name="android:textColor">@color/settingslib_text_color_primary_device_default</item>
</style>
</resources>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<resources>
<style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
<item name="elevationOverlayEnabled">true</item>
<item name="elevationOverlayColor">?attr/colorPrimary</item>
<item name="colorPrimary">@color/settingslib_primary_device_default_settings_light</item>
<item name="colorAccent">@color/settingslib_accent_device_default_light</item>
</style>
</resources>

View file

@ -0,0 +1,172 @@
/*
* Copyright (C) 2021 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.android.settingslib.collapsingtoolbar;
import android.app.ActionBar;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.FragmentActivity;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.resources.TextAppearanceConfig;
import com.android.settingslib.R;
/**
* A base Activity that has a collapsing toolbar layout is used for the activities intending to
* enable the collapsing toolbar function.
*/
public class CollapsingToolbarBaseActivity extends FragmentActivity {
private static final float TOOLBAR_LINE_SPACING_MULTIPLIER = 1.1f;
@Nullable
private CollapsingToolbarLayout mCollapsingToolbarLayout;
@Nullable
private AppBarLayout mAppBarLayout;
private int mCustomizeLayoutResId = 0;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mCustomizeLayoutResId > 0 && !true) {
super.setContentView(mCustomizeLayoutResId);
return;
}
// Force loading font synchronously for collapsing toolbar layout
TextAppearanceConfig.setShouldLoadFontSynchronously(true);
super.setContentView(R.layout.collapsing_toolbar_base_layout);
mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
mAppBarLayout = findViewById(R.id.app_bar);
if (mCollapsingToolbarLayout != null) {
mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
}
disableCollapsingToolbarLayoutScrollingBehavior();
final Toolbar toolbar = findViewById(R.id.action_bar);
setActionBar(toolbar);
// Enable title and home button by default
final ActionBar actionBar = getActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
actionBar.setDisplayShowTitleEnabled(true);
}
}
@Override
public void setContentView(int layoutResID) {
final ViewGroup parent = findViewById(R.id.content_frame);
if (parent != null) {
parent.removeAllViews();
}
LayoutInflater.from(this).inflate(layoutResID, parent);
}
@Override
public void setContentView(View view) {
final ViewGroup parent = findViewById(R.id.content_frame);
if (parent != null) {
parent.addView(view);
}
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
final ViewGroup parent = findViewById(R.id.content_frame);
if (parent != null) {
parent.addView(view, params);
}
}
/**
* This method allows an activity to replace the default layout with a customize layout. Notice
* that it will no longer apply the features being provided by this class when this method
* gets called.
*/
protected void setCustomizeContentView(int layoutResId) {
mCustomizeLayoutResId = layoutResId;
}
@Override
public void setTitle(CharSequence title) {
if (mCollapsingToolbarLayout != null) {
mCollapsingToolbarLayout.setTitle(title);
} else {
super.setTitle(title);
}
}
@Override
public void setTitle(int titleId) {
if (mCollapsingToolbarLayout != null) {
mCollapsingToolbarLayout.setTitle(getText(titleId));
} else {
super.setTitle(titleId);
}
}
@Override
public boolean onNavigateUp() {
if (!super.onNavigateUp()) {
finishAfterTransition();
}
return true;
}
/**
* Returns an instance of collapsing toolbar.
*/
@Nullable
public CollapsingToolbarLayout getCollapsingToolbarLayout() {
return mCollapsingToolbarLayout;
}
/**
* Return an instance of app bar.
*/
@Nullable
public AppBarLayout getAppBarLayout() {
return mAppBarLayout;
}
private void disableCollapsingToolbarLayoutScrollingBehavior() {
if (mAppBarLayout == null) {
return;
}
final CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
behavior.setDragCallback(
new AppBarLayout.Behavior.DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
return false;
}
});
params.setBehavior(behavior);
}
}

View file

@ -0,0 +1,130 @@
/*
* Copyright (C) 2021 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.android.settingslib.collapsingtoolbar;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.android.settingslib.R;
/**
* A base fragment that has a collapsing toolbar layout for enabling the collapsing toolbar design.
*/
public abstract class CollapsingToolbarBaseFragment extends Fragment {
private static final float TOOLBAR_LINE_SPACING_MULTIPLIER = 1.1f;
@Nullable
private CoordinatorLayout mCoordinatorLayout;
@Nullable
private CollapsingToolbarLayout mCollapsingToolbarLayout;
@Nullable
private AppBarLayout mAppBarLayout;
@NonNull
private Toolbar mToolbar;
@NonNull
private FrameLayout mContentFrameLayout;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.collapsing_toolbar_base_layout, container,
false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
mCoordinatorLayout = view.findViewById(R.id.content_parent);
}
mCollapsingToolbarLayout = view.findViewById(R.id.collapsing_toolbar);
mAppBarLayout = view.findViewById(R.id.app_bar);
if (mCollapsingToolbarLayout != null) {
mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
}
disableCollapsingToolbarLayoutScrollingBehavior();
mToolbar = view.findViewById(R.id.action_bar);
mContentFrameLayout = view.findViewById(R.id.content_frame);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
requireActivity().setActionBar(mToolbar);
}
/**
* Return an instance of CoordinatorLayout.
*/
@Nullable
public CoordinatorLayout getCoordinatorLayout() {
return mCoordinatorLayout;
}
/**
* Return an instance of app bar.
*/
@Nullable
public AppBarLayout getAppBarLayout() {
return mAppBarLayout;
}
/**
* Return the collapsing toolbar layout.
*/
@Nullable
public CollapsingToolbarLayout getCollapsingToolbarLayout() {
return mCollapsingToolbarLayout;
}
/**
* Return the content frame layout.
*/
@NonNull
public FrameLayout getContentFrameLayout() {
return mContentFrameLayout;
}
private void disableCollapsingToolbarLayoutScrollingBehavior() {
if (mAppBarLayout == null) {
return;
}
final CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
behavior.setDragCallback(
new AppBarLayout.Behavior.DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
return false;
}
});
params.setBehavior(behavior);
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (C) 2021 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.android.settingslib.collapsingtoolbar;
import androidx.fragment.app.FragmentActivity;
/**
* A base Activity for Settings-specific page transition. Activities extending it will get
* Settings transition applied.
*/
public abstract class SettingsTransitionActivity extends FragmentActivity {
private static final String TAG = "SettingsTransitionActivity";
protected boolean isSettingsTransitionEnabled() {
return false;
}
}

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@color/settingslib_protection_color"/>
<corners android:radius="28dp"/>
<size android:width="@dimen/settingslib_illustration_width"
android:height="@dimen/settingslib_illustration_height"/>
</shape>
</item>
</layer-list>

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:background="?android:attr/colorBackground"
android:importantForAccessibility="noHideDescendants"
android:gravity="center"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/illustration_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:paddingHorizontal="@dimen/settingslib_illustration_padding"
android:orientation="vertical">
<ImageView
android:id="@+id/background_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:src="@drawable/protection_background"/>
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:maxWidth="@dimen/settingslib_illustration_width"
android:maxHeight="@dimen/settingslib_illustration_height"
android:adjustViewBounds="true"/>
<FrameLayout
android:id="@+id/middleground_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:layout_gravity="center"
android:visibility="gone"/>
</FrameLayout>
</FrameLayout>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<resources>
<color name="settingslib_protection_color">@android:color/black</color>
</resources>

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<resources>
<color name="settingslib_protection_color">@android:color/white</color>
<!-- Dynamic colors-->
<color name="settingslib_color_blue600">#1a73e8</color>
<color name="settingslib_color_blue400">#669df6</color>
<color name="settingslib_color_blue300">#8ab4f8</color>
<color name="settingslib_color_blue100">#d2e3fc</color>
<color name="settingslib_color_blue50">#e8f0fe</color>
<color name="settingslib_color_green600">#1e8e3e</color>
<color name="settingslib_color_green400">#5bb974</color>
<color name="settingslib_color_green100">#ceead6</color>
<color name="settingslib_color_green50">#e6f4ea</color>
<color name="settingslib_color_red600">#d93025</color>
<color name="settingslib_color_red400">#ee675c</color>
<color name="settingslib_color_red100">#fad2cf</color>
<color name="settingslib_color_red50">#fce8e6</color>
<color name="settingslib_color_yellow600">#f9ab00</color>
<color name="settingslib_color_yellow400">#fcc934</color>
<color name="settingslib_color_yellow100">#feefc3</color>
<color name="settingslib_color_yellow50">#fef7e0</color>
<color name="settingslib_color_grey900">#202124</color>
<color name="settingslib_color_grey800">#3c4043</color>
<color name="settingslib_color_grey700">#5f6368</color>
<color name="settingslib_color_grey600">#80868b</color>
<color name="settingslib_color_grey400">#bdc1c6</color>
<color name="settingslib_color_grey300">#dadce0</color>
<color name="settingslib_color_grey200">#e8eaed</color>
<color name="settingslib_color_orange600">#e8710a</color>
<color name="settingslib_color_orange400">#fa903e</color>
<color name="settingslib_color_orange300">#fcad70</color>
<color name="settingslib_color_orange100">#fedfc8</color>
<color name="settingslib_color_pink600">#e52592</color>
<color name="settingslib_color_pink400">#ff63b8</color>
<color name="settingslib_color_pink300">#ff8bcb</color>
<color name="settingslib_color_pink100">#fdcfe8</color>
<color name="settingslib_color_purple600">#9334e6</color>
<color name="settingslib_color_purple400">#af5cf7</color>
<color name="settingslib_color_purple300">#c58af9</color>
<color name="settingslib_color_purple100">#e9d2fd</color>
<color name="settingslib_color_cyan600">#12b5c8</color>
<color name="settingslib_color_cyan400">#4ecde6</color>
<color name="settingslib_color_cyan300">#78d9ec</color>
<color name="settingslib_color_cyan100">#cbf0f8</color>
</resources>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<resources>
<!-- Padding of illustration -->
<dimen name="settingslib_illustration_padding">16dp</dimen>
<dimen name="settingslib_illustration_width">412dp</dimen>
<dimen name="settingslib_illustration_height">300dp</dimen>
</resources>

View file

@ -0,0 +1,123 @@
/*
* Copyright (C) 2021 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.android.settingslib.widget;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.util.Pair;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.model.KeyPath;
import com.airbnb.lottie.value.LottieFrameInfo;
import com.airbnb.lottie.value.SimpleLottieValueCallback;
import java.util.HashMap;
import com.android.settingslib.R;
/**
* ColorUtils is a util class which help the lottie illustration
* changes the color of tags in the json file.
*/
public class ColorUtils {
private static HashMap<String, Integer> sSysColors;
private static HashMap<String, Pair<Integer, Integer>> sFixedColors;
static {
sFixedColors = new HashMap<>();
sFixedColors.put(".blue600", new Pair<Integer, Integer>(
R.color.settingslib_color_blue600, R.color.settingslib_color_blue400));
sFixedColors.put(".green600", new Pair<Integer, Integer>(
R.color.settingslib_color_green600, R.color.settingslib_color_green400));
sFixedColors.put(".red600", new Pair<Integer, Integer>(
R.color.settingslib_color_red600, R.color.settingslib_color_red400));
sFixedColors.put(".yellow600", new Pair<Integer, Integer>(
R.color.settingslib_color_yellow600, R.color.settingslib_color_yellow400));
sFixedColors.put(".blue400", new Pair<Integer, Integer>(
R.color.settingslib_color_blue400, R.color.settingslib_color_blue100));
sFixedColors.put(".green400", new Pair<Integer, Integer>(
R.color.settingslib_color_green400, R.color.settingslib_color_green100));
sFixedColors.put(".red400", new Pair<Integer, Integer>(
R.color.settingslib_color_red400, R.color.settingslib_color_red100));
sFixedColors.put(".yellow400", new Pair<Integer, Integer>(
R.color.settingslib_color_yellow400, R.color.settingslib_color_yellow100));
sFixedColors.put(".blue300", new Pair<Integer, Integer>(
R.color.settingslib_color_blue300, R.color.settingslib_color_blue50));
sFixedColors.put(".blue50", new Pair<Integer, Integer>(
R.color.settingslib_color_blue50, R.color.settingslib_color_grey900));
sFixedColors.put(".green50", new Pair<Integer, Integer>(
R.color.settingslib_color_green50, R.color.settingslib_color_grey900));
sFixedColors.put(".red50", new Pair<Integer, Integer>(
R.color.settingslib_color_red50, R.color.settingslib_color_grey900));
sFixedColors.put(".yellow50", new Pair<Integer, Integer>(
R.color.settingslib_color_yellow50, R.color.settingslib_color_grey900));
// Secondary colors
sFixedColors.put(".orange600", new Pair<Integer, Integer>(
R.color.settingslib_color_orange600, R.color.settingslib_color_orange300));
sFixedColors.put(".pink600", new Pair<Integer, Integer>(
R.color.settingslib_color_pink600, R.color.settingslib_color_pink300));
sFixedColors.put(".purple600", new Pair<Integer, Integer>(
R.color.settingslib_color_purple600, R.color.settingslib_color_purple300));
sFixedColors.put(".cyan600", new Pair<Integer, Integer>(
R.color.settingslib_color_cyan600, R.color.settingslib_color_cyan300));
sFixedColors.put(".orange400", new Pair<Integer, Integer>(
R.color.settingslib_color_orange400, R.color.settingslib_color_orange100));
sFixedColors.put(".pink400", new Pair<Integer, Integer>(
R.color.settingslib_color_pink400, R.color.settingslib_color_pink100));
sFixedColors.put(".purple400", new Pair<Integer, Integer>(
R.color.settingslib_color_purple400, R.color.settingslib_color_purple100));
sFixedColors.put(".cyan400", new Pair<Integer, Integer>(
R.color.settingslib_color_cyan400, R.color.settingslib_color_cyan100));
sFixedColors.put(".gery400", new Pair<Integer, Integer>(
R.color.settingslib_color_grey400, R.color.settingslib_color_grey700));
sFixedColors.put(".gery300", new Pair<Integer, Integer>(
R.color.settingslib_color_grey300, R.color.settingslib_color_grey600));
sFixedColors.put(".gery200", new Pair<Integer, Integer>(
R.color.settingslib_color_grey200, R.color.settingslib_color_grey800));
}
private static boolean isDarkMode(Context context) {
return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
}
/**
* Apply the color of tags to the animation.
*/
public static void applyDynamicColors(Context context, LottieAnimationView animationView) {
for (String key : sFixedColors.keySet()) {
final Pair<Integer, Integer> fixedColorPair = sFixedColors.get(key);
final int color = isDarkMode(context) ? fixedColorPair.second : fixedColorPair.first;
animationView.addValueCallback(
new KeyPath("**", key, "**"),
LottieProperty.COLOR_FILTER,
new SimpleLottieValueCallback<ColorFilter>() {
@Override
public ColorFilter getValue(LottieFrameInfo<ColorFilter> frameInfo) {
return new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN);
}
}
);
}
}
}

View file

@ -0,0 +1,391 @@
/*
* Copyright (C) 2021 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.android.settingslib.widget;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.RawRes;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieDrawable;
import java.io.FileNotFoundException;
import java.io.InputStream;
import com.android.settingslib.R;
/**
* IllustrationPreference is a preference that can play lottie format animation
*/
public class IllustrationPreference extends Preference {
private static final String TAG = "IllustrationPreference";
private static final boolean IS_ENABLED_LOTTIE_ADAPTIVE_COLOR = false;
private static final int SIZE_UNSPECIFIED = -1;
private int mMaxHeight = SIZE_UNSPECIFIED;
private int mImageResId;
private boolean mIsAutoScale;
private Uri mImageUri;
private Drawable mImageDrawable;
private View mMiddleGroundView;
private final Animatable2.AnimationCallback mAnimationCallback =
new Animatable2.AnimationCallback() {
@Override
public void onAnimationEnd(Drawable drawable) {
((Animatable) drawable).start();
}
};
private final Animatable2Compat.AnimationCallback mAnimationCallbackCompat =
new Animatable2Compat.AnimationCallback() {
@Override
public void onAnimationEnd(Drawable drawable) {
((Animatable) drawable).start();
}
};
public IllustrationPreference(Context context) {
super(context);
init(context, /* attrs= */ null);
}
public IllustrationPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public IllustrationPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
public IllustrationPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final ImageView backgroundView =
(ImageView) holder.findViewById(R.id.background_view);
final FrameLayout middleGroundLayout =
(FrameLayout) holder.findViewById(R.id.middleground_layout);
final LottieAnimationView illustrationView =
(LottieAnimationView) holder.findViewById(R.id.lottie_view);
// To solve the problem of non-compliant illustrations, we set the frame height
// to 300dp and set the length of the short side of the screen to
// the width of the frame.
final int screenWidth = getContext().getResources().getDisplayMetrics().widthPixels;
final int screenHeight = getContext().getResources().getDisplayMetrics().heightPixels;
final FrameLayout illustrationFrame = (FrameLayout) holder.findViewById(
R.id.illustration_frame);
final LayoutParams lp = (LayoutParams) illustrationFrame.getLayoutParams();
lp.width = screenWidth < screenHeight ? screenWidth : screenHeight;
illustrationFrame.setLayoutParams(lp);
handleImageWithAnimation(illustrationView);
handleImageFrameMaxHeight(backgroundView, illustrationView);
if (mIsAutoScale) {
illustrationView.setScaleType(mIsAutoScale
? ImageView.ScaleType.CENTER_CROP
: ImageView.ScaleType.CENTER_INSIDE);
}
handleMiddleGroundView(middleGroundLayout);
if (IS_ENABLED_LOTTIE_ADAPTIVE_COLOR) {
ColorUtils.applyDynamicColors(getContext(), illustrationView);
}
}
/**
* Sets the middle ground view to preference. The user
* can overlay a view on top of the animation.
*/
public void setMiddleGroundView(View view) {
if (view != mMiddleGroundView) {
mMiddleGroundView = view;
notifyChanged();
}
}
/**
* Removes the middle ground view of preference.
*/
public void removeMiddleGroundView() {
mMiddleGroundView = null;
notifyChanged();
}
/**
* Enables the auto scale feature of animation view.
*/
public void enableAnimationAutoScale(boolean enable) {
if (enable != mIsAutoScale) {
mIsAutoScale = enable;
notifyChanged();
}
}
/**
* Sets the lottie illustration resource id.
*/
public void setLottieAnimationResId(int resId) {
if (resId != mImageResId) {
resetImageResourceCache();
mImageResId = resId;
notifyChanged();
}
}
/**
* Gets the lottie illustration resource id.
*/
public int getLottieAnimationResId() {
return mImageResId;
}
/**
* Sets the image drawable to display image in {@link LottieAnimationView}.
*
* @param imageDrawable the drawable of an image
*/
public void setImageDrawable(Drawable imageDrawable) {
if (imageDrawable != mImageDrawable) {
resetImageResourceCache();
mImageDrawable = imageDrawable;
notifyChanged();
}
}
/**
* Gets the image drawable from display image in {@link LottieAnimationView}.
*
* @return the drawable of an image
*/
public Drawable getImageDrawable() {
return mImageDrawable;
}
/**
* Sets the image uri to display image in {@link LottieAnimationView}.
*
* @param imageUri the Uri of an image
*/
public void setImageUri(Uri imageUri) {
if (imageUri != mImageUri) {
resetImageResourceCache();
mImageUri = imageUri;
notifyChanged();
}
}
/**
* Gets the image uri from display image in {@link LottieAnimationView}.
*
* @return the Uri of an image
*/
public Uri getImageUri() {
return mImageUri;
}
/**
* Sets the maximum height of the views, still use the specific one if the maximum height was
* larger than the specific height from XML.
*
* @param maxHeight the maximum height of the frame views in terms of pixels.
*/
public void setMaxHeight(int maxHeight) {
if (maxHeight != mMaxHeight) {
mMaxHeight = maxHeight;
notifyChanged();
}
}
private void resetImageResourceCache() {
mImageDrawable = null;
mImageUri = null;
mImageResId = 0;
}
private void handleMiddleGroundView(ViewGroup middleGroundLayout) {
middleGroundLayout.removeAllViews();
if (mMiddleGroundView != null) {
middleGroundLayout.addView(mMiddleGroundView);
middleGroundLayout.setVisibility(View.VISIBLE);
} else {
middleGroundLayout.setVisibility(View.GONE);
}
}
private void handleImageWithAnimation(LottieAnimationView illustrationView) {
if (mImageDrawable != null) {
resetAnimations(illustrationView);
illustrationView.setImageDrawable(mImageDrawable);
final Drawable drawable = illustrationView.getDrawable();
if (drawable != null) {
startAnimation(drawable);
}
}
if (mImageUri != null) {
resetAnimations(illustrationView);
illustrationView.setImageURI(mImageUri);
final Drawable drawable = illustrationView.getDrawable();
if (drawable != null) {
startAnimation(drawable);
} else {
// The lottie image from the raw folder also returns null because the ImageView
// couldn't handle it now.
startLottieAnimationWith(illustrationView, mImageUri);
}
}
if (mImageResId > 0) {
resetAnimations(illustrationView);
illustrationView.setImageResource(mImageResId);
final Drawable drawable = illustrationView.getDrawable();
if (drawable != null) {
startAnimation(drawable);
} else {
// The lottie image from the raw folder also returns null because the ImageView
// couldn't handle it now.
startLottieAnimationWith(illustrationView, mImageResId);
}
}
}
private void handleImageFrameMaxHeight(ImageView backgroundView, ImageView illustrationView) {
if (mMaxHeight == SIZE_UNSPECIFIED) {
return;
}
final Resources res = backgroundView.getResources();
final int frameWidth = res.getDimensionPixelSize(R.dimen.settingslib_illustration_width);
final int frameHeight = res.getDimensionPixelSize(R.dimen.settingslib_illustration_height);
final int restrictedMaxHeight = Math.min(mMaxHeight, frameHeight);
backgroundView.setMaxHeight(restrictedMaxHeight);
illustrationView.setMaxHeight(restrictedMaxHeight);
// Ensures the illustration view size is smaller than or equal to the background view size.
final float aspectRatio = (float) frameWidth / frameHeight;
illustrationView.setMaxWidth((int) (restrictedMaxHeight * aspectRatio));
}
private void startAnimation(Drawable drawable) {
if (!(drawable instanceof Animatable)) {
return;
}
if (drawable instanceof Animatable2) {
((Animatable2) drawable).registerAnimationCallback(mAnimationCallback);
} else if (drawable instanceof Animatable2Compat) {
((Animatable2Compat) drawable).registerAnimationCallback(mAnimationCallbackCompat);
} else if (drawable instanceof AnimationDrawable) {
((AnimationDrawable) drawable).setOneShot(false);
}
((Animatable) drawable).start();
}
private static void startLottieAnimationWith(LottieAnimationView illustrationView,
Uri imageUri) {
final InputStream inputStream =
getInputStreamFromUri(illustrationView.getContext(), imageUri);
illustrationView.setFailureListener(
result -> Log.w(TAG, "Invalid illustration image uri: " + imageUri, result));
illustrationView.setAnimation(inputStream, /* cacheKey= */ null);
illustrationView.setRepeatCount(LottieDrawable.INFINITE);
illustrationView.playAnimation();
}
private static void startLottieAnimationWith(LottieAnimationView illustrationView,
@RawRes int rawRes) {
illustrationView.setFailureListener(
result -> Log.w(TAG, "Invalid illustration resource id: " + rawRes, result));
illustrationView.setAnimation(rawRes);
illustrationView.setRepeatCount(LottieDrawable.INFINITE);
illustrationView.playAnimation();
}
private static void resetAnimations(LottieAnimationView illustrationView) {
resetAnimation(illustrationView.getDrawable());
illustrationView.cancelAnimation();
}
private static void resetAnimation(Drawable drawable) {
if (!(drawable instanceof Animatable