I have a tablet application which I am rebranding so there are multiple themes, based on the type of user.
I'd like to find the name of the theme that is currently being applied, and based on that theme then I can do some back-end functionality changes.
I have to dynamically set some image resources, which is fine as long as I pass in the correct theme resource (the R.style.redtheme), but I'd like to set this dynamically.
TypedArray a = getTheme().obtainStyledAttributes(R.style.redtheme, new int[] {aTabResource.mDrawableAttrId});
To do the styling I'm creating custom attributes and then overriding them in the styles.
If there is no simple way to get the theme, I will just save a preference.
The package manager has access to quite a bit of metadata.
It can be accessed like this:
int theme = 0; //0==not set
try
{
String packageName = getClass().getPackage().getName();
PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA);
theme = packageInfo.applicationInfo.theme;
}
catch (Exception e)
{
e.printStackTrace();
}
After this runs, theme will have the style resource.
Related
I know there is a way to set themes by defining in styles.xml and use it like that
setTheme(android.R.style.MyTheme);
However, I want to get themes from another app which I developed as well. I know the resources names and actually I am able to get theme id with this code block;
Resources res = getPackageManager().getResourcesForApplication("com.example.theme");
int resThemeId = res.getIdentifier("my_theme","style","com.example.theme");
When I debug, I see that resThemeId is not zero.
Then, I need the final command to set this theme. Before super.onCreate() function, I try to implement this method but it seems it is not working
setTheme(resThemeId);
But instead of this, if I write below statement, I works fine
setTheme(android.R.style.Theme_Holo_Light);
So, what should I do to use a theme from different package resource?
So, what should I do to use a theme from different package resource?
You shouldn't do this for many reasons. I wrote a simple project that shows that it is indeed possible as long as the package contains the resources your activity uses.
See: https://github.com/jaredrummler/SO-41872033
Basically, you would need to return the package's resources from the activity:
public class MainActivity extends AppCompatActivity {
Resources resources;
#Override protected void onCreate(Bundle savedInstanceState) {
int themeResId = getResources().getIdentifier("AppTheme", "style", "com.example.theme");
if (themeResId != 0) {
setTheme(themeResId);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override public Resources getResources() {
if (resources == null) {
try {
resources = getPackageManager().getResourcesForApplication("com.example.theme");
} catch (PackageManager.NameNotFoundException e) {
resources = super.getResources();
}
}
return resources;
}
}
This is just to show that it is possible. Again, I recommend that you avoid this.
As already mentioned in a comment you can access resources from other applications, but using another applications theme will not work.
Since having proof is always a good thing let's have a look at the source code (I used API 24 sources)
Calling setTheme() on an Activity will invoke initializeTheme() in the ContextThemeWrapper parent class, which will end up calling onApplyThemeResource(..) which in turn will try to load the actual theme data from resources by calling theme.applyStyle(resId, true)
Following the chain through the wrapper Resources.Theme we can see in ResourcesImpl.ThemeImpl the following, where AssetManager is called to load the style into the theme:
void applyStyle(int resId, boolean force) {
synchronized (mKey) {
AssetManager.applyThemeStyle(mTheme, resId, force);
mThemeResId = resId;
mKey.append(resId, force);
}
}
This is where you try and fail to load the foreign theme from your other app.
Since most of the methods you would need to use are static calls or package local methods it does not seem that there is any way to achieve what you want, (e.g. applying or creating a new Theme)
Even if you get a hold of the other application's AssetManager by using getAssets() on a context there is no accessible method to create or apply themes.
So the only way to use another app's resources would be to add the resources to yours.
Have you seen this demo: Multiple Theme Material Design
You can check this demo for runtime theme change.
Hope it will helps you.
I'm swappping between applications in Android, I pass a series of parameters to customize the other application (colors, logos, etc.), but I can't find the styles I pass, I can only find the drawables, colors, layouts.
Is there a way to find the styles?
I'm using this
Intent intent = new Intent();
intent.setClassName("com.app.to.call", "com.app.to.call.LoginActivity");
intent.putExtra("package", getPackageName());
intent.putExtra("style", R.style.AppTheme);
startActivity(intent);
From the othe app I'm using
if(customization.getKeys().getString(Global.LINK_PACKAGE) == null) {
customization = null;
}else{
Resources resources = getPackageManager().getResourcesForApplication(customization.getKeys().getString(Global.LINK_PACKAGE));
container.setBackgroundColor(resources.getColor(customization.getKeys().getInt(Global.LINK_BACKGROUND_COLOR)));
setTheme();//How can I get the style?
}
where customization.getKeys() are the keys I pass.
Remember that R class between two application can have the same name variable but a different value. I don't recommend pass R values between apps
Application's icon can be gotten by following code:
Drawable drawable = ResolveInfo.loadIcon(packageManager);
But it is the drawable that maybe modified by ROM (like: right-angle changed to round angle corner), so my question is how can I get original logo icon by program?
P.S. some launcher can do this, like MIUI launcher.
You can get application icon using PackageManager
PackageManager manager = context.getPackageManager();
Drawable appIcon = manager.getApplicationIcon(packagename);
public static Drawable getApplicationIcon(Context context, String packname){
PackageManager pm = context.getPackageManager();
try {
PackageInfo pInfo = pm.getPackageInfo(packname, 0);
int resId = pInfo.applicationInfo.icon;
AssetManager assetManager = AssetManager.class.newInstance();
AssetManager.class.getMethod("addAssetPath", new Class[]{String.class}).invoke(assetManager, pInfo.applicationInfo.sourceDir);
Resources oRes = context.getResources();
Resources resources = new Resources(assetManager, oRes.getDisplayMetrics(), oRes.getConfiguration());
Drawable result = resources.getDrawable(resId);
assetManager.close();
return result;
} catch (Exception e) {
e.printStackTrace();
}
return pm.getDefaultActivityIcon();
}
Sorry for the late reply. But I hope this will help someone new to android.
This method will get the icon associated with the Activity.
public static Drawable getOriginalActivityIcon(Context context,ResolveInfo resolveInfo){
PackageManager packageManager=context.getPackageManager();
try {
ActivityInfo info=resolveInfo.activityInfo;
Drawable drawable = packageManager.getDrawable(info.packageName,info.getIconResource(), packageManager.getApplicationInfo(info.packageName, 0));
if(drawable==null){
drawable=resolveInfo.loadIcon(packageManager);
}
return drawable;
}catch (Exception e){
e.printStackTrace();
}
return packageManager.getDefaultActivityIcon();
}
Use this code
getPackageManager().getApplicationIcon(packagename);
Drawable icon = getPackageManager().getApplicationIcon(packageInfo.packageName);
The conclusion is that if a round icon exists and no adaptive icon exists, you can't get a square icon. Unless this system is customized with a different Android framework than AOSP, such as the MIUI.
At first I was just like you, looking around for a way to load the square icon. I've tried every answer including the ones here.
Finally I found that if both square and round icons are set, then the Android system framework returns the ResId of the round icon when you request the ResId of the icon. At last I went searching for the code of the Android system framework and verified this guess:
When a developer specifies an adaptive application icon, and a non-adaptive round application icon, create an alias from the round icon to the regular icon for v26 APIs and up. We do this because certain devices prefer android:roundIcon over android:icon regardless of the API levels of the drawables set for either. This auto-aliasing behaviour allows an app to prefer the android:roundIcon on API 25 devices, and prefer the adaptive icon on API 26 devices.
If the application contains adaptive icons and the API level >= 26, you can use it to draw out square icon. For example.
I'm a new mobile developer and i know how to put theme on an application i was able to make 2 mobile application as of now but i wanted to try to make a customized theme for phones. I'm wondering if anyone out there has idea on the following.
1.how to make a customized theme for phone.
2.what is the format of the theme.
3.is it possible to make it in eclipse.
4.what are the things needed to consider to make a theme for phone.
5.how to implement customized theme on phone.
6.available tutorial on making a customized phone theme.
If you want to change theme of a specific theme application like GO Launcher or aHome you can find a link on their site, see this question for more information Themes in Android?
but you can also write your launcher application, if you want to do this, you can see this links: Build An Application Launcher For Android
But if you want to change theme of your application, so you can read these documents and tutorials:
developer.android.com/guide/topics/ui/themes.html
Android Styles and Themes - Tutorial
Another way that i think you must know for building several themes is building an apk for every theme of your application, this solution make your app size smaller. like this function:
public static Resources getResource() {
resourcePackageName = "com.mine.MyApp";
PackageManager packageManager = mContext.getPackageManager();
Resources resources = null, resources2 = null;
try {
resources = mContext.getResources();
resources2 = packageManager
.getResourcesForApplication("com.mine.MyAppTheme");
resourcePackageName = "com.mine.MyAppTheme";
} catch (Exception e) {
Log.d("ss", e.toString());
}
if (resources2 != null)
mResource = resources2;
else
mResource = resources;
return mResource;
}
In this code, your app name is com.mine.MyApp and you have a app with all of resources in the com.mine.MyApp that is your theme, name of theme apk app is com.mine.MyAppTheme. you can use several apk as several theme. just put your resources on theme apk file. you can see this question:
Writing themed applications in Android
I realize I'm probably doing something fundamentally wrong with styles and themes but I'm still a bit of an Android newbie so please excuse my ignorance. I'm trying to change the style of my MediaRouteButton from the default dark to light since I have a light ActionBar. My MediaRouteButton is implemented in the ActionBar as follows:
<item
android:id="#+id/menu_item_cast"
android:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
android:actionViewClass="android.support.v7.app.MediaRouteButton"
android:showAsAction="always"
android:actionButtonStyle="#android:style/Theme.MediaRouter.Light"/>
However, this gives me:
android/res/menu/main.xml:24: error: Error: No resource found that matches the given name (at 'actionButtonStyle' with value '#android:style/Theme.MediaRouter.Light').
If you don't want to change the color of the icon, framework would choose the right one (dark or light) based on the theme of your actionbar, so for an actionbar with light background, it will choose a darker icon and vice versa; here is a sample app with two different themes, Theme.AppCompat.Light and Theme.AppCompat, respectively (everything else is identical):
As you can see, the appropriate one is selected automatically. If you want to use a different color based on your branding requirements, the easiest would be to add the following images to your project (with usual resolutions under mdpi, hdpi, ..):
mr_ic_media_route_disabled_holo_dark.png
mr_ic_media_route_off_holo_dark.png
mr_ic_media_route_on_0_holo_dark.png
mr_ic_media_route_on_1_holo_dark.png
mr_ic_media_route_on_2_holo_dark.png
(if you are using a light actionbar theme, replace "dark" with "light"). Take a look at the assets at Google Cast >
Sample Apps (section Cast Icons) to get a feel for what these images are and build your own ones based on those.
I ended up decompiling android-support-v7-mediarouter.jar to see what was going on. With the code available I was able to extend MediaRouteButton and set the private Drawable through reflection hacking. There has to be a better way right?
public class CustomMediaRouteButton extends MediaRouteButton {
private static final String TAG = "CustomMediaRouteButton";
public CustomMediaRouteButton(Context context){
this(context, null);
}
public CustomMediaRouteButton(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.mediaRouteButtonStyle);
}
public CustomMediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
Drawable d = getResources().getDrawable(R.drawable.mr_ic_media_route_holo_light);
setRemoteIndicatorDrawable(d);
}
private void setRemoteIndicatorDrawable(Drawable d) {
try {
Field field = MediaRouteButton.class.getDeclaredField("mRemoteIndicator");
field.setAccessible(true);
Drawable remoteIndicator = (Drawable)field.get(this);
if (remoteIndicator != null) {
remoteIndicator.setCallback(null);
unscheduleDrawable(remoteIndicator);
}
field.set(this, d);
if (d != null) {
d.setCallback(this);
d.setState(getDrawableState());
d.setVisible(getVisibility() == 0, false);
}
} catch (Exception e) {
Log.e(TAG, "problem changing drawable:" + e.getMessage());
}
refreshDrawableState();
}
}
You can change it easily now with your custom drawable.
Just call this method on your cast button.
mediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button);
mediaRouteButton.setRemoteIndicatorDrawable(yourDrawable);
I found a way to change your color of MediaRouteButton by code, and is easy to be done, no need to touch existing code.
The MediaRouteButton will style itself following the theme of context which you passed. You can create a ContextThemeWrapper to wrap the context and then pass it to MediaRouteActionProvider.
Following is an example:
MenuItem item = menu.add(Menu.NONE, R.id.menu_cast, Menu.NONE, "Cast");
MenuItemCompat.setActionProvider(item, new MediaRouteActionProvider(new ContextThemeWrapper(this, R.style.AppTheme)));
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
Here the R.style.AppTheme is a theme which extends from Theme.AppCompat, it is a dark theme so the cast button will always show in light version. You can also pass in light theme to make cast button behave in dark version. Also you can change it dynamically, just invalidate the option menu, it should recreate the action provider using the new theme.
I am using support library 23.1.1 and have not found any problem in this way.
If you want to change the icons used (not just the style), you need to name them the exact same way they are named here. For instance, for the light theme, you need to have a set of icons for every resolution with names: ic_cast_on_light.png, ic_cast_on_0_light.png, ic_cast_on_1_light.png, ic_cast_on_2_light.png, ic_cast_disabled_light.png, ic_cast_off_light.png.
You should be able to change the style by applying the style to your activity, e.g. in AndroidManifest.xml. If you want to change the drawable, I succeeded by adding mr_ic_media_route_holo_light drawable to my project. Just add it to drawables folder and customize it as you need it. Example: https://github.com/android/platform_frameworks_support/blob/master/v7/mediarouter/res/drawable/mr_ic_media_route_holo_light.xml