Ok so I am trying to replicate the look and feel of the Muzei Live Wallpaper App by Roman Nurik which is open source.
(Check out his GitHub repository here - https://github.com/romannurik/muzei/ )
When the App Launches there is a subtle svg path tracing animation along with a Ken Burns effect that goes on in the background.
You can notice that the activity bleeds into the Status Bar and Navigation Bar.
I've been able to achieve the background animation but haven't been able to figure out how to make the activity full screen like shown in the 2nd GIF below
I need help making this activity fullscreen/ bleed into the status bar and navigation bar.
Here's what I have been able to achieve
This what I want to implement
Here's my code
MainActivity.Java
package devexchanges.info.kenburnview;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.Toast;
import com.flaviofaria.kenburnsview.KenBurnsView;
import com.flaviofaria.kenburnsview.RandomTransitionGenerator;
import com.flaviofaria.kenburnsview.Transition;
public class MainActivity extends AppCompatActivity {
private KenBurnsView kenBurnsView;
private boolean isPlay = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
kenBurnsView = (KenBurnsView) findViewById(R.id.image);
AccelerateDecelerateInterpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
RandomTransitionGenerator generator = new RandomTransitionGenerator(11000, ACCELERATE_DECELERATE);
kenBurnsView.setTransitionGenerator(generator); //set new transition on kenburns view
kenBurnsView.setTransitionListener(onTransittionListener());
}
private KenBurnsView.TransitionListener onTransittionListener() {
return new KenBurnsView.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) {
//Toast.makeText(MainActivity.this, "start", Toast.LENGTH_SHORT).show();
}
#Override
public void onTransitionEnd(Transition transition) {
//Toast.makeText(MainActivity.this, "end", Toast.LENGTH_SHORT).show();
}
};
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.flaviofaria.kenburnsview.KenBurnsView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/saigon"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<Button android:background="#drawable/circle_button"
android:layout_height="#dimen/intro_activate_button_size"
android:layout_width="#dimen/intro_activate_button_size"
android:text="ACTIVATE"
android:textAllCaps="true"
android:fontFamily="sans-serif-condensed"
android:textStyle="bold"
android:textSize="18dp"
android:textColor="#333"
android:id="#+id/activate_muzei_button"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="103dp"
android:elevation="2dp" />
Just add this in your style.xml:
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="android:windowFullscreen">true</item>
Just add this to your onCreate() method:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
To make an activity fullscreen put this in your manifest:
<activity android:name=".ActivityName"
android:label="#string/app_name"
android:theme="#android:style/Theme.NoTitleBar.Fullscreen"/>
Source:
Fullscreen Activity in Android?
Well turns out there is a simple solution to the problem. I just needed to make the status bar and navigation bar transparent.
Post API 21 we can do it programmatically like this -
getWindow().setStatusBarColor(Color.TRANSPARENT);
for making it work on lower android versions, I just needed to add the transparency via xml in the /res/values-v21/styles.xml
<item name="android:statusBarColor">#android:color/transparent</item>
Here's the final effect
use #style/Theme.AppCompat.Light.NoActionBar if you are using AppCompat activity
<activity android:name=".view.activity.SplashActivity"
android:theme="#style/Theme.AppCompat.Light.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
Just add this code in your activity onCreate()
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
You can do it programatically:
public class ActivityName extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// remove title
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
}
}
Read This Content: Fullscreen Activity in Android?
Kotlin: programmatically for API 21: Put it inside onCreate()
#TargetApi(21)
window.statusBarColor = Color.TRANSPARENT
In 2021 all codes are either deprecated or not working. For example:
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
// Set the content to appear under the system bars so that the
// content doesn't resize when the system bars hide and show.
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
// Hide the nav bar and status bar
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN);
every line deprecated. some other some need API level R. So use style.
Search some time on google I found
<style name="FullScreenTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#color/design_default_color_primary</item>
<item name="colorPrimaryDark">#color/design_default_color_primary_dark</item>
<item name="colorAccent">#color/teal_200</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">#color/white</item>
</style>
now use this style in the Manifest file
<activity
android:name=".FullScreenActivigy"
android:theme="#style/FullScreenTheme"/>
I am using this block of code in my production app which is starts from API 21 to 30. work fine. Even user interact with their SMS or notification. when they back to my app it become full screen again.
If you're in the latest version of android in themes.xml change it to
parent="Theme.MaterialComponents.DayNight.DarkActionBar"
to
parent="Theme.MaterialComponents.DayNight.NoActionBar"
Related
I'm trying to upgrade my application to target android 31 which introduces splash screen API so I followed the migration process mentioned here, but after migration the application doesn't run because splash screen API only supports android version 21 and above so what is the process to support older versions than 21?
Androidx SplashScreen class works as following:
On API 31+ (Android 12+) this class calls the platform methods.
Prior API 31, the platform behavior is replicated with the exception of the Animated Vector Drawable support on the launch screen.
the problem is Animated Vector Drawable class was introduced with Android 21 so the current Androidx SplashScreen class backward compatible with Android +21 so i figured out some solution.
I created two different splash screen activities one to handle the old versions and the second which use Androidx SplashScreen API. and I made the system lunches splash screen based on the current android version, so I did the following
1- to allow application compiling and build, I had to add this line to my manifest file
<uses-sdk tools:overrideLibrary="androidx.core.splashscreen"/>
2- create the new Splash screen activity which uses Androidx SplashScreen API by following the migration steps from documentation.
3- create bools.xml under values folder
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="new_splash_enabled">false</bool>
<bool name="old_splash_enabled">false</bool>
</resources>
4- override bools.xml in values-vX (X is the minSdkVersion) in my case was 16 so i created values-v16 folder in the same level of values folder and create bools.xml under it like below
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="new_splash_enabled">false</bool>
<bool name="old_splash_enabled">true</bool>
</resources>
5- override bools.xml in values-vX (X is the min version you want to apply the new SplashScreen so it is any number between 21 and 31)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="new_splash_enabled">true</bool>
<bool name="old_splash_enabled">false</bool>
</resources>
6- in your manifest, i made the system decides which splash activity to launch based on values in bools.xml file
<activity
android:name=".NewSplash"
android:theme="#style/Theme.App.Starting"
android:enabled="#bool/new_splash_enabled"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".OldSplash"
android:enabled="#bool/old_splash_enabled">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
If you're using the Androidx Compat Library make sure you're using the attributes without the android tag.
For example:
This will work fine
<item name="windowSplashScreenBackground">?colorPrimary</item>
This will give you a "require API 31" error
<item name="android:windowSplashScreenBackground">?colorPrimary</item>
Lazy approach: in short programmatically set theme -> done.
Advantage: Only one activity. Basically a one liner
Disadvantage: SDK <=21 No splash, but everyone else
Long answer, I don't like the two Activities logic. So:
Follow step 1 to 2 of #Ramy-Ibrahim's answer.
set in application block of your manifest:
android:theme="#style/Theme.App.Starting"
My launcher activity has no theme set anymore instead in onCreate():
//v6.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setTheme(R.style.Theme_App_Starting);
SplashScreen.installSplashScreen(this);
}else{
setTheme(R.style.AppTheme);
}
//mind everything BEFORE super.onCreate()
super.onCreate(savedInstanceState);
Tested in Android 11(SDK30) and Android 4.4 (SDk19) and Android 12.2 (SDK32)
What worked for me , and inspired by #Ramy Ibrahim answer
in your manifest file before <application> tag add
<uses-sdk tools:overrideLibrary="androidx.core.splashscreen" />
in your theme/style
<style name="ThemeStarting" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">#color/_background_color</item>
<item name="windowSplashScreenAnimatedIcon">#drawable/icon</item>
<item name="windowSplashScreenAnimationDuration">1000</item>
<item name="postSplashScreenTheme">#style/YOUR_THEME</item>
</style>
NewSplashActivity
public class NewSplashActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
super.onCreate(savedInstanceState);
Intent intent = new Intent(getBaseContext(), MainActivity.class);
startActivity(intent);
finish();
}
else {
setTheme(R.style.OldSplashTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.new_spl);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Intent intent = new Intent(getBaseContext(), MainActivity.class);
startActivity(intent);
finish();
}
}, 1999);
}
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
}
}
This question already has answers here:
Android - Prevent white screen at startup
(18 answers)
Closed 4 years ago.
I created a splash screen for my application using an empty activity that stays visible for 3 seconds with a background image.
Usually, the application starts with a white screen before the background image becomes visible, however, some applications are already started with the "real" splash screen image.
How to implement this?
you can use splash screen in this way.
Add style for splash screen activity in styles.xml
<style name="SplashTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">#drawable/yourImageName</item>
</style>
Add that style as theme to your SplashScreenActivity in manifest file
<activity android:name=".SplashScreenActivity"
android:theme="#style/SplashTheme"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Remove setContentView() in SplashScreenActivity's onCreate() Method and use it as a java file which extends AppCompactActivity
The problem is because system process draws initial blank screen when launching the app, from documentation:
A common way to implement a themed launch screen is to use the windowDisablePreview theme attribute to turn off the initial blank
screen that the system process draws when launching the app. However,
this approach can result in a longer startup time than apps that don’t
suppress the preview window. Also, it forces the user to wait with no
feedback while the activity launches, making them wonder if the app is
functioning properly.
You can disable it with windowDisablePreview attribute, something like this:
<application
...
android:windowDisablePreview="true">
...
</application>
Splash Activity usually starts with the background.
Try this code, it might help you.
Code for your splash activity page.
public class SplashScreen extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
Thread myThread = new Thread(){
#Override
public void run() {
try {
sleep(3000);
Intent intent = new Intent(getApplicationContext(),MainActivity.class);
startActivity(intent);
finish();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
myThread.start();
}}
In layout file, you need to include just Image which you want to see on the splash page.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_splash_screen"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/capture"
/>
</RelativeLayout>
And make sure you add the Splashscreen page before Main activity in Manifest.
<activity android:name=".SplashScreen">
I am trying to follow the search interface manual from android.com. I have few problems and I am not sure what I did wrong exactly.
If you want to look, here is the project as zip file.
1- The android:hint is not visible when I touch search icon. It comes empty. Why? If I set it programmatically it works eg. searchView.setQueryHint(getString(R.string.search_hint)); Why it doesn't work from XML?
2- Search field does not get focus and keyboard does not appear automatically when I touch search icon. I need to touch to the search textfield to get focus. I believe it should get focus automatically?
3- When I write something and touch the search icon on the keyboard. I do not see any log entries logged at logcat window. What is missing?
4- (SOLVED) I tried android:iconifiedByDefault="false" in XML and also searchView.setIconifiedByDefault(false); and it is always iconified when I start the application. Why is that happening?
I found out that iI need to remove collapseActionView from app:showAsAction otherwise setIconifiedByDefault(false) does not work. Also if you remove collapseActionView and set setIconifiedByDefault(false) then it never collapses.
I am guessing I missed something somewhere but I am not sure exactly what...
So below are the steps I followed:
I started a new project with 'Basic Activity' in Android Studio 3.
It has a ready toolbar and 'Hello World' in the middle.
Then to xml folder I added the searchable.xml file with contents:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="#string/app_name"
android:hint="Search hint text"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer">
</searchable>
Then changed AndroidManifest.xml file and added an activity:
<activity android:name=".SearchableActivity">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="#xml/searchable"/>
</activity>
Then created a simple SearchableActivity.class. Just to log some lines.
package com.test.myapplication;
import android.app.ListActivity;
import android.app.SearchManager;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
public class SearchableActivity extends ListActivity {
private static String TAG = SearchableActivity.class.getSimpleName();
#Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
handleIntent(getIntent());
}
#Override
protected void onNewIntent(Intent intent) {
handleIntent(intent);
}
private void handleIntent(Intent intent) {
Log.d(TAG, "handleIntent");
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
//use the query to search your data somehow
}
}
}
Then added to existing menu_main.xml the necessary item:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.test.myapplication.MainActivity">
<item android:id="#+id/search"
android:title="#string/app_name"
android:icon="#android:drawable/ic_menu_search"
app:showAsAction="collapseActionView|ifRoom"
app:actionViewClass="android.widget.SearchView" />
<item
android:id="#+id/action_settings"
android:orderInCategory="100"
android:title="#string/action_settings"
app:showAsAction="never" />
</menu>
Update: If I change to app:actionViewClass="android.support.v7.widget.SearchView" and also in MainActivity.class if I import import android.support.v7.widget.SearchView; then when I click on the search icon, it shows default Search... text gets focus automatically. But does NOT show hint from XML and still there are no log entries when I submit any information.
UPDATE:
I realized that if I put the intent-filter and the meta part under MainActivity I am getting the search intent inside main activity. But I don't understand why it does not start the SearchableActivity. Because manual page for SearchView clearly says:
When a user executes a search from the search dialog or widget, the system starts your searchable activity and sends it a ACTION_SEARCH intent.
Use this
app:actionViewClass="android.support.v7.widget.SearchView"
Instead of this
app:actionViewClass="android.widget.SearchView"
Apparently all I needed to do was to forward the intent to correct activity from onCreateOptionsMenu()
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
ComponentName componentName = new ComponentName(this, SearchableActivity.class);
SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
if (searchManager != null)
searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName));
I'm having some issues returning from Immersive Mode in an ActionBarActivity. I have created a simple app to illustrate this issue. There is a layout with a single button to toggle immersive mode. When "returning" from immersive mode, the action bar is offset downwards from its original position, roughly the same distance again that it is usually offset from the top of the screen.
I've tried this on a Nexus 4 running Lollipop. This behavior did not happen pre-Lollipop.
Screenshots before, immersed, after.
A simple ActionBarActivity that illustrates this issue:
public class MainActivity extends ActionBarActivity {
private boolean immersed;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#TargetApi(Build.VERSION_CODES.KITKAT)
private void enterImmersiveMode() {
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
immersed = true;
}
}
#TargetApi(Build.VERSION_CODES.KITKAT)
private void leaveImmersiveMode() {
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
immersed = false;
// recreate();
}
}
public void toggleImmersive(View v) {
if (immersed) {
leaveImmersiveMode();
} else {
enterImmersiveMode();
}
}
}
Nothing fancy in the manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.immersivetest"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="ImmersiveTest"
android:theme="#style/Theme.AppCompat" >
<activity
android:name=".MainActivity"
android:label="ImmersiveTest" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Trivial layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.immersivetest.MainActivity" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="toggle immersive"
android:onClick="toggleImmersive"
/>
</RelativeLayout>
The workaround I use at the moment is to call recreate() after leaving immersive mode, but it looks a bit "glitchy".
If I up the minSdkVersion and use Activity instead of ActionBarActivity, i.e. don't use the support library, then I do not experience this behavior.
I understand that immersive mode is only available in KitKat+ and that I don't need to use the ActionBarActivity from the support library, but the finished product will have to run on API versions 8+ and the immersive mode is an optional extra.
Some other workarounds I have thought about and dismissed for now:
Have a wrapper launcher Activity which immediately forwards programmatically to an ActionBarActivity for lower API versions.
Have multiple apps by API version.
Either of those options sounds like a lot of added maintenance.
Is there anything else I could try? Are there any glaring mistakes in the code above, with respect to returning from immersive mode?
Update
I have since updated the Nexus 4 to 5.1 and the support library to rev 22.1.1 and the behavior is still the same. I have also updated the code to use the new AppCompatActivity since ActionBarActivity is now deprecated. The behavior is, once more, the same.
public class MainActivity extends AppCompatActivity {
// no changes here
}
Update
This weird behavior also extends to the landscape mode. In addition to the offset at the top of the screen, there also is an offset to the right of the screen between the end of the action bar and the navigation buttons. Interestingly this offset again seems to be the size of the navigation button "bar", i.e. larger than the offset at the top.
Landscape screenshots before, immersed, after.
Had the same problem, fixed with this:
your leaveImmersiveMode() function should use these flags instead:
getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_FULLSCREEN);
Use a Toolbar ... I had the same problem and switching to a Toolbar instead of a customContentView solved it.
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
And set your Activity's theme to #style/Theme.AppCompat.NoActionBar
Notwithstanding the fact that ActionBarActivity was deprecated in the support library from V26, changing your leaveImmersiveMode() method to specify the following flags fixes the problem for me with the test app using your code (which originally showed the extra gap you mentioned).
#TargetApi(Build.VERSION_CODES.KITKAT)
private void leaveImmersiveMode() {
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
immersed = false;
// recreate();
}
}
These are the flags recommended in the Android documentation on immersive mode.
I have developed an app that uses one view and loads one of two fragments in to it, depending on the orientation, i.e. portrait/landscape.
Both fragments have the same UI TextView/EditText/Button components.
When the application is in portrait mode and I press a text field for a long(ish) time, the text is selected with selection range markers and the default text selection CAB replaces the application bar.
All as expected.
However when I flip to landscape mode, although my application still works as expected, the default text selection behavior does not work. I can still select text by long pressing a field but although the text is highlighted, there are no selection range markers and the default text selection CAB does not appear. I can do nothing with the selected text.
When I flip back to portrait mode, everything works as expected again.
I am targeting a minimum sdk of 16 and building with 19.
Is this a known bug or have I missed a step when flipping from portrait to landscape?
EDIT:
I did some further investigation in a sandbox and have found what seems to be a bug, though not sure if it is in Android or the phone itself. It also seems that this bug is triggered in both portrait and landscape modes.
I have isolated the problem in the following small app.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.example.foo"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="org.example.foo.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Foo</string>
<string name="action_settings">Settings</string>
<string name="select_me">Select Me</string>
</resources>
ActivityMain.java
package org.example.foo;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public static class PlaceholderFragment extends Fragment
{
public PlaceholderFragment()
{
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
}
}
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.example.foo.MainActivity"
tools:ignore="MergeRootFrame" />
fragment_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="org.example.foo.PlaceholderFragment" >
<TextView
android:id="#+id/tv"
android:textIsSelectable="true"
android:layout_width="200dp"
android:layout_height="50dp"
android:text="#string/select_me"
/>
</LinearLayout>
So it seems that the the layout_width and layout_height properties combined are blowing the default text selection CAB functionality.
With these parameters I can run the application and select the text, in portrait and landscape modes, with the default text selection CAB displayed as expected.
The bug is triggered if I increase either the layout_width or layout_height by one or more. Either increase will still allow me to select a word but it stops the default text selection CAB from operating, including the text selection range markers.
Further investigation shows that the bug might have some permanent effects on the current Android state, causing an internal memory leak or similar.
Consider this:
1) run the application with the properties as I have listed here and the application works as expected
2) increment either of the layout_width or layout_height properties by 1, rebuild the application and it no longer runs as expected, it now shows the bug
3) reset the adjusted property in (2), so it is now in the original state (1), rebuild the application and it still shows the bug
4) reboot the phone and the application released in (3) now works as expected
I can't think of anything else to do at this point, except report this as a bug to Android and then try to redesign my app to get around this problem.
A last thought is that this could be a hardware related bug, so perhaps someone could test this for me on a different device?
I am currently experiencing this bug on a Huawei Y300-100 phone, running Android 4.1.1
EDIT
I have reported this bug to google, issue number 68435
I think there's more to the issue than just some dimension parameters. The below works without any issue:
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.TextView;
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout holder = new FrameLayout(this);
int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
201,
getResources().getDisplayMetrics());
int hieght = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
51,
getResources().getDisplayMetrics());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width,hieght);
params.gravity = Gravity.CENTER;
TextView textView = new TextView(this);
textView.setTextIsSelectable(true);
textView.setText("Select Me");
textView.setBackgroundColor(Color.GRAY);
holder.addView(textView,params);
setContentView(holder);
}
}
In AndroidManifest.xml, remove orientation of android:configChanges="keyboardHidden|screenSize|orientation" to avoid re-calculating offset for your cursor