sdk: introduce Style API

This API will allow apps to change global style mode and accent.

Global style mode can be
* Automatic (wallpaper)
* Automatic (day of time)
* Light
* Dark

Accent colors are defined in the caller application that will have
to pass the package name.

It's possible for apps to pass a Bitmap and get a suggestion
of a global style + accent color that can be applied.

Restrictions:
* Only one accent can be enabled at time.
* We're not limiting this to system apps, but we're marking
  this as dangerous permissions so apps will have to require
  it at runtime to the user.

Change-Id: I921e8758c3ae093a88e897899612830258c97f8d
Signed-off-by: Joey <joey@lineageos.org>
This commit is contained in:
Joey
2018-02-22 14:30:42 +01:00
parent 17b61ed328
commit 76fc3d3942
12 changed files with 640 additions and 6 deletions

View File

@@ -96,6 +96,17 @@ public final class LineageContextConstants {
*/
public static final String LINEAGE_AUDIO_SERVICE = "lineageaudio";
/**
* Use with {@link android.content.Context#getSystemService} to retrieve a
* {@link lineageos.app.StyleInterface} interact with system style.
*
* @see android.content.Context#getSystemService
* @see lineageos.app.StyleInterface
*
* @hide
*/
public static final String LINEAGE_STYLE_INTERFACE = "lineagestyle";
/**
* Features supported by the Lineage SDK.
*/
@@ -147,5 +158,13 @@ public final class LineageContextConstants {
*/
@SdkConstant(SdkConstant.SdkConstantType.FEATURE)
public static final String AUDIO = "org.lineageos.audio";
/**
* Feature for {@link PackageManager#getSystemAvailableFeatures} and
* {@link PackageManager#hasSystemFeature}: The device includes the lineage style service
* utilized by the lineage sdk.
*/
@SdkConstant(SdkConstant.SdkConstantType.FEATURE)
public static final String STYLES = "org.lineageos.style";
}
}

View File

@@ -178,6 +178,11 @@ public class Build {
/**
* Unreleased preliminary version starting from LineageOS 15.1
* <p>Applications targeting this or a later version will get access to these
* new features!</p>
* <ul>
* <li>Change system colors via {@link lineageos.app.StyleInterface}
* </ul>
*/
public static final int HACKBERRY = 8;
}

View File

@@ -0,0 +1,28 @@
/*
**
** Copyright (C) 2018 The LineageOS 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 lineageos.style;
import android.graphics.Bitmap;
import lineageos.style.Suggestion;
/** {@hide} */
interface IStyleInterface {
boolean setGlobalStyle(int style);
boolean setAccent(String pkgName);
Suggestion getSuggestion(in Bitmap source, in int[] colors);
}

View File

@@ -0,0 +1,199 @@
/**
* Copyright (c) 2015, The CyanogenMod 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 lineageos.style;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import lineageos.app.LineageContextConstants;
public class StyleInterface {
/**
* Global style: automatic (based on wallpaper) mode
*
* @see #setGlobalStyle
*/
public static final int STYLE_GLOBAL_AUTO_WALLPAPER = 0;
/**
* Global style: automatic (based on day time) mode
*
* @see #setGlobalStyle
*/
public static final int STYLE_GLOBAL_AUTO_DAYTIME = 1;
/**
* Global style: light
*
* @see #setGlobalStyle
*/
public static final int STYLE_GLOBAL_LIGHT = 2;
/**
* Global style: dark
*
* @see #setGlobalStyle
*/
public static final int STYLE_GLOBAL_DARK = 3;
/**
* Default accent
* Used to remove any active accent and use default one
*
* @see #setAccent
*/
public static final String ACCENT_DEFAULT = "lineageos";
/**
* Allows an application to change system style.
* This is a dangerous permission, your app must request
* it at runtime as any other dangerous permission
*/
public static final String CHANGE_STYLE_SETTINGS_PERMISSION =
"lineageos.permission.CHANGE_STYLE";
private static final String TAG = "StyleInterface";
private static IStyleInterface sService;
private static StyleInterface sInstance;
private Context mContext;
private StyleInterface(Context context) {
Context appContext = context.getApplicationContext();
if (appContext != null) {
mContext = appContext;
} else {
mContext = context;
}
sService = getService();
if (context.getPackageManager().hasSystemFeature(
LineageContextConstants.Features.STYLES) && sService == null) {
throw new RuntimeException("Unable to get StyleInterfaceService. The service" +
" either crashed, was not started, or the interface has been called to early" +
" in SystemServer init");
}
}
/**
* Get or create an instance of the {@link lineageos.app.StyleInterface}
* @param context
* @return {@link StyleInterface}
*/
public static StyleInterface getInstance(Context context) {
if (sInstance == null) {
sInstance = new StyleInterface(context);
}
return sInstance;
}
/** @hide **/
public static IStyleInterface getService() {
if (sService != null) {
return sService;
}
IBinder b = ServiceManager.getService(LineageContextConstants.LINEAGE_STYLE_INTERFACE);
sService = IStyleInterface.Stub.asInterface(b);
if (b != null) {
sService = IStyleInterface.Stub.asInterface(b);
return sService;
} else {
Log.e(TAG, "null service. SAD!");
return null;
}
}
/**
* Set global style.
*
* You will need {@link #CHANGE_STYLE_SETTINGS_PERMISSION}
* to utilize this functionality.
*
* @see #STYLE_GLOBAL_AUTO_WALLPAPER
* @see #STYLE_GLOBAL_AUTO_DAYTIME
* @see #STYLE_GLOBAL_LIGHT
* @see #STYLE_GLOBAL_DARK
* @param style The style mode to set the device to.
* One of {@link #STYLE_GLOBAL_AUTO_WALLPAPER},
* {@link #STYLE_GLOBAL_AUTO_DAYTIME},
* {@link #STYLE_GLOBAL_LIGHT} or
* {@link #STYLE_GLOBAL_DARK}
*/
public boolean setGlobalStyle(int style) {
if (sService == null) {
return false;
}
try {
return sService.setGlobalStyle(style);
} catch (RemoteException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
}
return false;
}
/**
* Set color accent package.
*
* You will need {@link #CHANGE_STYLE_SETTINGS_PERMISSION}
* to utilize this functionality.
*
* @param pkgName The package name of the accent
*/
public boolean setAccent(String pkgName) {
if (sService == null) {
return false;
}
try {
return sService.setAccent(pkgName);
} catch (RemoteException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
}
return false;
}
/**
* Get the best color that suites a bitmap object and the appropriate global style
*
* @param source The object you want the suggested color to be matched with
* @param colors A list of colors that the selection will be made from
*
* @return suggestion for global style + accent combination
*/
public Suggestion getSuggestion(Bitmap source, int[] colors) {
if (colors.length == 0) {
throw new IllegalArgumentException(
"The colors array argument must contain at least one element");
}
Suggestion fallback = new Suggestion(STYLE_GLOBAL_LIGHT, colors[0]);
if (sService == null) {
return fallback;
}
try {
return sService.getSuggestion(source, colors);
} catch (RemoteException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
}
return fallback;
}
}

View File

@@ -0,0 +1,20 @@
/*
**
** Copyright (C) 2018 The LineageOS 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 lineageos.style;
parcelable Suggestion;

View File

@@ -0,0 +1,94 @@
/*
**
** Copyright (C) 2018 The LineageOS 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 lineageos.style;
import android.os.Parcel;
import android.os.Parcelable;
import lineageos.os.Build;
import lineageos.os.Concierge;
import lineageos.os.Concierge.ParcelInfo;
public class Suggestion implements Parcelable {
public final int globalStyle;
public final int selectedAccent;
/**
* Default constructor
*
* @see lineageos.style.StyleInterface#getSuggestion
*
* @param globalStyle one of {@link #STYLE_GLOBAL_LIGHT} or {@link #STYLE_GLOBAL_DARK}
* @param colorPosition position of selected color in the input array
*/
public Suggestion(int globalStyle, int selectedAccent) {
this.globalStyle = globalStyle;
this.selectedAccent = selectedAccent;
}
private Suggestion(Parcel parcel) {
ParcelInfo parcelInfo = Concierge.receiveParcel(parcel);
int parcelableVersion = parcelInfo.getParcelVersion();
if (parcelableVersion >= Build.LINEAGE_VERSION_CODES.HACKBERRY) {
globalStyle = parcel.readInt();
selectedAccent = parcel.readInt();
} else {
globalStyle = 0;
selectedAccent = 0;
}
// Complete parcel info for the concierge
parcelInfo.complete();
}
/** @hide */
public static final Parcelable.Creator<Suggestion> CREATOR =
new Parcelable.Creator<Suggestion>() {
@Override
public Suggestion createFromParcel(Parcel source) {
return new Suggestion(source);
}
@Override
public Suggestion[] newArray(int size) {
return new Suggestion[size];
}
};
/** @hide */
@Override
public int describeContents() {
return 0;
}
/** @hide */
@Override
public void writeToParcel(Parcel dest, int flags) {
// Tell the concierge to prepare the parcel
ParcelInfo parcelInfo = Concierge.prepareParcel(dest);
// ==== HACKBERRY ====
dest.writeInt(globalStyle);
dest.writeInt(selectedAccent);
// Complete parcel info for the concierge
parcelInfo.complete();
}
}