Search Dialog won't appear with TabHost implementation - java

I have created my tabs using the following example code and in the comments it was noted that many people could no longer call upon the search dialog and I have been able to no longer have my program force close when you click on the hardware search button but when you click on the button it doesn't pop up anything anymore.
Would anyone know how to fix this?
Multiple Android Activities with TabActivity
import java.util.ArrayList;
import android.app.Activity;
import android.app.ActivityGroup;
import android.app.LocalActivityManager;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Window;
/**
* The purpose of this Activity is to manage the activities in a tab.
* Note: Child Activities can handle Key Presses before they are seen here.
* #author Eric Harlow
*/
public class TabGroupActivity extends ActivityGroup {
private ArrayList<String> mIdList;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mIdList == null) mIdList = new ArrayList<String>();
}
/**
* This is called when a child activity of this one calls its finish method.
* This implementation calls {#link LocalActivityManager#destroyActivity} on the child activity
* and starts the previous activity.
* If the last child activity just called finish(),this activity (the parent),
* calls finish to finish the entire group.
*/
#Override
public void finishFromChild(Activity child) {
LocalActivityManager manager = getLocalActivityManager();
int index = mIdList.size()-1;
if (index < 1) {
finish();
return;
}
manager.destroyActivity(mIdList.get(index), true);
mIdList.remove(index);
index--;
String lastId = mIdList.get(index);
Intent lastIntent = manager.getActivity(lastId).getIntent();
Window newWindow = manager.startActivity(lastId, lastIntent);
setContentView(newWindow.getDecorView());
}
/**
* Starts an Activity as a child Activity to this.
* #param Id Unique identifier of the activity to be started.
* #param intent The Intent describing the activity to be started.
* #throws android.content.ActivityNotFoundException.
*/
public void startChildActivity(String Id, Intent intent) {
Window window = getLocalActivityManager().startActivity(Id,intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
if (window != null) {
mIdList.add(Id);
setContentView(window.getDecorView());
}
}
/**
* The primary purpose is to prevent systems before android.os.Build.VERSION_CODES.ECLAIR
* from calling their default KeyEvent.KEYCODE_BACK during onKeyDown.
*/
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
//preventing default implementation previous to android.os.Build.VERSION_CODES.ECLAIR
return true;
}
return super.onKeyDown(keyCode, event);
}
/**
* Overrides the default implementation for KeyEvent.KEYCODE_BACK
* so that all systems call onBackPressed().
*/
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
onBackPressed();
return true;
}
return super.onKeyUp(keyCode, event);
}
/**
* If a Child Activity handles KeyEvent.KEYCODE_BACK.
* Simply override and add this method.
*/
#Override
public void onBackPressed () {
int length = mIdList.size();
if ( length > 1) {
Activity current = getLocalActivityManager().getActivity(mIdList.get(length-1));
current.finish();
}
}
}
#Override
public void onBackPressed() {
TabGroupActivity parentActivity = (TabGroupActivity)getParent();
parentActivity.onBackPressed();
}
The following section explains how I used this ActivityGroup. The first thing I did was subclass this ActivityGroup like this.
import android.content.Intent;
import android.os.Bundle;
public class TabGroup1Activity extends TabGroupActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startChildActivity("OptionsActivity", new Intent(this,OptionsActivity.class));
}
}
I added this TabGroup1Activity into my TabActivity like this.
TabHost tabHost = getTabHost();
tabHost.addTab(tabHost.newTabSpec("tab1")
.setIndicator("MESSAGES")
.setContent(new Intent(this, TabGroup1Activity.class)));
Then any Activity that you want to start in the ActivityGroup can be done in a way similar to this.
Intent frequentMessages = new Intent(getParent(), FrequentMessageActivity.class);
TabGroupActivity parentActivity = (TabGroupActivity)getParent();
parentActivity.startChildActivity("FrequentMessageActivity", frequentMessages);
I have made all my activities searchable in the Android Manifest and I followed the tutorial on implementing search.
Creating a Search Interface
Prior to modifying my code to work with the TabActivity the search was working as intended but something in the code has broken the search functionality.

I have found a solution which I am working on deciphering but I was hoping that someone with more know how would help in implementing it based on the example given in the tutorial example posted in the question.
Here is the solution that I found but haven't applied
Nested TabHosts

Related

How do I save variable values across Activities in Android Studio

I'm new here and also new to programming. I'm currently working on a project and I've been stuck for bout a week now.The only thing I want to do is save two variables so that it still can be seen after the app is closed and reopened. Also for some reason when I open the Settings Activity my variables values are set back to zero.
I'm aware that others have posted similar questions like this but I just can't adapt it to my work. I don't understand a lot of things I read like SharedPreferences, onPause(), and GAME_STATE_KEY. Could anyone please explain how to do such a thing without linking the Android Documentation articles? I don't even understand what the documentation says and copy/pasting code there doesn't seem to work.
This is my MainActivity
package com.example.courtcounter;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity<format> extends AppCompatActivity {
TextView textView;
int scoreTeamA = 0;
int scoreTeamB = 0;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy\n hh:mm aa");
String format = simpleDateFormat.format(new Date());
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.team_a_score);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String shareMessage = createMessage(format, scoreTeamA, scoreTeamB);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_SUBJECT, "Match Score");
intent.setType("text/*");
intent.putExtra(Intent.EXTRA_TEXT, shareMessage);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.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){
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
private String createMessage(String date, int TeamA, int TeamB){
EditText editTeamA = findViewById(R.id.team_a_name);
String teamAName =editTeamA.getText().toString();
EditText editTeamB = findViewById(R.id.team_b_name);
String teamBName = editTeamB.getText().toString();
String shareMessage =format +"\n"+ teamAName+ " : "+ TeamA + "\n" + teamBName + " : "+ TeamB;
return shareMessage;
}
/** Resets score of boths teams to 0
*/
public void resetScore(View v){
scoreTeamA = 0;
scoreTeamB = 0;
displayForTeamA(scoreTeamA);
displayForTeamB(scoreTeamB);
}
/**
* Displays the given score for Team A.
*/
public void displayForTeamA(int scoreTeamA){
TextView scoreViewA = (TextView)findViewById(R.id.team_a_score);
String teamA = scoreViewA.getText().toString();
scoreViewA.setText(String.valueOf(scoreTeamA));
}
/**
* Displays the given score for Team B.
*/
public void displayForTeamB(int score) {
TextView scoreViewB = (TextView) findViewById(R.id.team_b_score);
String teamB = scoreViewB.getText().toString();
scoreViewB.setText(String.valueOf(score));
}
/**
* This method is called when the +3 points button is clicked.
*/
public void ThreeA(View view){
scoreTeamA = scoreTeamA +3;
displayForTeamA(scoreTeamA);
}
/**
* This method is called when the +2 points button is clicked.
*/
public void TwoA(View view){
scoreTeamA = scoreTeamA +2;
displayForTeamA(scoreTeamA);
}
/**
* This method is called when the FREE THROW button is clicked.
*/
public void OneA(View view){
scoreTeamA = scoreTeamA + 1;
displayForTeamA(scoreTeamA);
}
/**
* This method is called when the +3 points button is clicked.
*/
public void ThreeB(View view){
scoreTeamB = scoreTeamB +3;
displayForTeamB(scoreTeamB);
}
/**
* This method is called when the +2 points button is clicked.
*/
public void TwoB(View view){
scoreTeamB = scoreTeamB +2;
displayForTeamB(scoreTeamB);
}
/**
* This method is called when the FREE THROW button is clicked.
*/
public void OneB(View view){
scoreTeamB = scoreTeamB + 1;
displayForTeamB(scoreTeamB);
}
}
Do I have to change My SettingActivity and SettingsFragment to help solve this or is it not needed?
Thanks.
If you want them to persist when the app is completely closed, SharedPreferences is the one you're looking for. This is a key/value store that allows you to store data that persists even after the activity is destroyed. Basically, they have two parts:
The key is a unique identifier used to access the data
The value is the actual data that you're trying to save
So first you get a reference to your shared preferences using
SharedPreferences.Editor editor = getSharedPreferences(
MY_PREFS_NAME, MODE_PRIVATE).edit();
This MY_PREFS_NAME can be any string you like. It allows you access your "slice" of the shared preferences. Once you get this reference, now you can begin reading and writing to them.
To write:
editor.putInt("scoreViewA", 5);
editor.putInt("scoreViewB", 12);
editor.apply();
And later to read:
SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
int scoreViewA = prefs.getInt("scoreViewA", 0);
int scoreViewB = prefs.getInt("scoreViewB", 0);
This second parameter in getInt is a default that will be used if the given key is not found. Note that once again you must use the same MY_PREFS_NAME when retrieving a reference to the shared preferences.
Finally, note that when writing to shared preferences, we call edit() before writing any changes, and we call apply() afterwards.
You'll want to put your code to write to shared preferences in your onPause method. This fires whenever the activity is no longer in the foreground. Then do your reading in the onResume method. This method fires when the app regains focus in the foreground.
#Override
public void onPause() {
super.onPause();
// write to shared preferences
}
#Override
public void onResume() {
super.onResume();
// read from shared preferences
}
And if you're just trying to share a variable from one activity to a new one, you can use a bundle. Check out this answer for a good example.
Hope that helps, welcome to Stackoverflow!
I finally figured it out, it was very Cathartic. My main issue was figuring out where to put the methods and it looks like I didn't need the onPause() and onResume() methods.
First in the AndroidManifest.xml file I added
android:launchMode="singleTop" but it the end it wasn't needed since I managed to save the preferences.
In my display methods I added
SharedPreferences myScoreB = getSharedPreferences("teamBScore", Context.MODE_PRIVATE); SharedPreferences.Editor editor = myScoreB.edit();editor.putInt("scoreB", scoreTeamB);editor.commit();
The reading data part was confusing but in the end I managed to do it in the oncreate method SharedPreferences myScoreB = this.getSharedPreferences("teamBScore", Context.MODE_PRIVATE);scoreTeamB = myScoreB.getInt("scoreB", 0);scoreViewB.setText(String.valueOf(scoreTeamB));
It now handle screen rotations without recreating the entire layout as well as restarts.

How to create a Listener to Preferences changes in Preferences activity?

I have Preferences activity in my app that has ListPreference so the user can choose language for the app.
The app displays the new language just after the user close the Preferences activity.
I want to create a listener to the ListPreference so the app will restart when the Listener is triggers (just after the user choose a language/choose item from the ListPreference).
How can I do that?
SettingsActivity:
public class SettingsActivity extends AppCompatPreferenceActivity {
/**
* A preference value change listener that updates the preference's summary
* to reflect its new value.
*/
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
if (preference instanceof ListPreference) {
// For list preferences, look up the correct display value in
// the preference's 'entries' list.
ListPreference listPreference = (ListPreference) preference;
int index = listPreference.findIndexOfValue(stringValue);
// Set the summary to reflect the new value.
preference.setSummary(
index >= 0
? listPreference.getEntries()[index]
: null);
} else {
// For all other preferences, set the summary to the value's
// simple string representation.
preference.setSummary(stringValue);
}
return true;
}
};
/**
* Binds a preference's summary to its value. More specifically, when the
* preference's value is changed, its summary (line of text below the
* preference title) is updated to reflect the value. The summary is also
* immediately updated upon calling this method. The exact display format is
* dependent on the type of preference.
*
* #see #sBindPreferenceSummaryToValueListener
*/
private static void bindPreferenceSummaryToValue(Preference preference) {
// Set the listener to watch for value changes.
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
// Trigger the listener immediately with the preference's
// current value.
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
PreferenceManager
.getDefaultSharedPreferences(preference.getContext())
.getString(preference.getKey(), ""));
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupActionBar();
setTitle(R.string.action_settings);
}
/**
* Set up the {#link android.app.ActionBar}, if the API is available.
*/
private void setupActionBar() {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
// Show the Up button in the action bar.
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
if (!super.onMenuItemSelected(featureId, item)) {
NavUtils.navigateUpFromSameTask(this);
}
return true;
}
return super.onMenuItemSelected(featureId, item);
}
/**
* This method stops fragment injection in malicious applications.
* Make sure to deny any unknown fragments here.
*/
protected boolean isValidFragment(String fragmentName) {
return PreferenceFragment.class.getName().equals(fragmentName)
|| GeneralPreferenceFragment.class.getName().equals(fragmentName);
}
/**
* This fragment shows general preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class GeneralPreferenceFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_general);
setHasOptionsMenu(true);
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
// to their values. When their values change, their summaries are
// updated to reflect the new value, per the Android Design
// guidelines.
bindPreferenceSummaryToValue(findPreference("example_text"));
bindPreferenceSummaryToValue(findPreference(getString(R.string.language_shared_pref_key)));
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
Intent intent = new Intent(getActivity(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
getActivity().finish();
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
}
}
pref_general.xml:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:defaultValue="#string/language_code"
android:entries="#array/pref_languages_list_titles"
android:entryValues="#array/pref_languages_list_values"
android:key="#string/language_shared_pref_key"
android:negativeButtonText="#null"
android:positiveButtonText="#null"
android:title="#string/pref_title_language" />
</PreferenceScreen>
Thank you!!!
Here is some real quick sample code for a shared prefs chaneg listener I have set-up in one of my projects; it's located in the onCreate of a Service but obviously can detect changes to my shared prefs that originate from anywhere in my app.
private SharedPreferences.OnSharedPreferenceChangeListener listener;
//Loads Shared preferences
prefs = PreferenceManager.getDefaultSharedPreferences(this);
//Setup a shared preference listener for hpwAddress and restart transport
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (key.equals(/*key for shared pref you're listening for*/) {
//Do stuff; restart activity in your case
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
One possible solution is to keep everything together, and implement the OnSharedPreferenceChangeListener interface inside your own GeneralPreferenceFragment.
You can do that by declaring that GeneralPreferenceFragment both inherits from PreferenceFragment, and implements SharedPreferences.OnSharedPreferenceChangeListener interface. This means that you need to define override for onSharedPreferenceChanged in GeneralPreferenceFragment.
Because preferences can change only if the fragment in question is active, you can register your fragment as listener in onResume for a fragment (via registerOnSharedPreferenceChangeListener), and unregister it on onStop.
The following example code is in Kotlin, not in Java, but it should be fairly easy to translate it.
class SettingsActivity : AppCompatActivity() {
// ...
class SettingsFragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener
{
// ...
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?,
key: String?) {
if (key == "<something>")
// do something
}
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences
?.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
preferenceScreen.sharedPreferences
?.unregisterOnSharedPreferenceChangeListener(this)
}
}

How to set/change Android Studio 'Fullscreen Activity' background?

So I created a new activity using the 'Fullscreen Activity' template in Android Studio. I now want to change the background which is set to android:background="#0099cc" by default in its generated XML file. However, I've tried the 'solution' on one of the more popular, similar questions but got a error.
How to set background color of an Activity to white programmatically?
The error I get is:
Caused by: android.content.res.Resources$NotFoundException: Resource ID #0xffffffff
at android.content.res.Resources.getValue(Resources.java:2060)
at android.content.res.Resources.getDrawable(Resources.java:1625)
at android.view.View.setBackgroundResource(View.java:16251)
which happens at the root.setBackgroundResource(Color.WHITE); line. Below is the entirety of the java code with the only two things added to the Activity template being the onStart method and a setActivityBackgroundColor() method.
package org.test.app;
import org.test.app.util.SystemUiHider;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import org.test.app.R;
/**
* An example full-screen activity that shows and hides the system UI (i.e.
* status bar and navigation/system bar) with user interaction.
*
* #see SystemUiHider
*/
public class FullscreenActivityTest extends Activity {
/**
* Whether or not the system UI should be auto-hidden after
* {#link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
*/
private static final boolean AUTO_HIDE = true;
/**
* If {#link #AUTO_HIDE} is set, the number of milliseconds to wait after
* user interaction before hiding the system UI.
*/
private static final int AUTO_HIDE_DELAY_MILLIS = 3000;
/**
* If set, will toggle the system UI visibility upon interaction. Otherwise,
* will show the system UI visibility upon interaction.
*/
private static final boolean TOGGLE_ON_CLICK = true;
/**
* The flags to pass to {#link SystemUiHider#getInstance}.
*/
private static final int HIDER_FLAGS = SystemUiHider.FLAG_HIDE_NAVIGATION;
/**
* The instance of the {#link SystemUiHider} for this activity.
*/
private SystemUiHider mSystemUiHider;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fullscreen_activity_test);
final View controlsView = findViewById(R.id.fullscreen_content_controls);
final View contentView = findViewById(R.id.fullscreen_content);
// Set up an instance of SystemUiHider to control the system UI for
// this activity.
mSystemUiHider = SystemUiHider.getInstance(this, contentView, HIDER_FLAGS);
mSystemUiHider.setup();
mSystemUiHider
.setOnVisibilityChangeListener(new SystemUiHider.OnVisibilityChangeListener() {
// Cached values.
int mControlsHeight;
int mShortAnimTime;
#Override
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public void onVisibilityChange(boolean visible) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
// If the ViewPropertyAnimator API is available
// (Honeycomb MR2 and later), use it to animate the
// in-layout UI controls at the bottom of the
// screen.
if (mControlsHeight == 0) {
mControlsHeight = controlsView.getHeight();
}
if (mShortAnimTime == 0) {
mShortAnimTime = getResources().getInteger(
android.R.integer.config_shortAnimTime);
}
controlsView.animate()
.translationY(visible ? 0 : mControlsHeight)
.setDuration(mShortAnimTime);
} else {
// If the ViewPropertyAnimator APIs aren't
// available, simply show or hide the in-layout UI
// controls.
controlsView.setVisibility(visible ? View.VISIBLE : View.GONE);
}
if (visible && AUTO_HIDE) {
// Schedule a hide().
delayedHide(AUTO_HIDE_DELAY_MILLIS);
}
}
});
// Set up the user interaction to manually show or hide the system UI.
contentView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (TOGGLE_ON_CLICK) {
mSystemUiHider.toggle();
} else {
mSystemUiHider.show();
}
}
});
// Upon interacting with UI controls, delay any scheduled hide()
// operations to prevent the jarring behavior of controls going away
// while interacting with the UI.
findViewById(R.id.dummy_button).setOnTouchListener(mDelayHideTouchListener);
}
protected void onStart() {
super.onStart();
setActivityBackgroundColor();
}
public void setActivityBackgroundColor() {
setContentView(R.layout.activity_fullscreen_activity_test);
View root = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
root.setBackgroundResource(Color.WHITE);
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
delayedHide(100);
}
/**
* Touch listener to use for in-layout UI controls to delay hiding the
* system UI. This is to prevent the jarring behavior of controls going away
* while interacting with activity UI.
*/
View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (AUTO_HIDE) {
delayedHide(AUTO_HIDE_DELAY_MILLIS);
}
return false;
}
};
Handler mHideHandler = new Handler();
Runnable mHideRunnable = new Runnable() {
#Override
public void run() {
mSystemUiHider.hide();
}
};
/**
* Schedules a call to hide() in [delay] milliseconds, canceling any
* previously scheduled calls.
*/
private void delayedHide(int delayMillis) {
mHideHandler.removeCallbacks(mHideRunnable);
mHideHandler.postDelayed(mHideRunnable, delayMillis);
}
}
setBackgroundResource takes a resource, not a color (even though both are integers). You probably want to use View.setBackgroundColor if you want to set it to a color in code.

Flip Images in a viewpager

I have a viewpager for a fullscreen image slider with horizontal swipe. What I am trying to do is put an image behind every image on the slider. Meaning, a user can click on a 'flip' button to reveal another image.
Below is my code however I have two issues:
The first time I click the flip button, it flips the image but only
to reveal the same image. However, when I click it the second time
it works :).
The flip button only works on the first image. When I swipe
right or left the flip button doesn't work anymore. Any help?
Note: I am using a fullscreenActivity theme which hides the UI control until the user clicks on the image.
FullscreenActivity.java
package me.zamaaan.wallpaper;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import me.zamaaan.wallpaper.util.SystemUiHider;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import static me.zamaaan.wallpaper.HeavyLifter.FAIL;
import static me.zamaaan.wallpaper.HeavyLifter.SUCCESS;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import java.util.ArrayList;
import java.util.List;
/** A helper class that will do the heavy work of decoding images and actually setting the wallpaper */
/**
* An example full-screen activity that shows and hides the system UI (i.e.
* status bar and navigation/system bar) with user interaction.
*
* #see SystemUiHider
*/
public class FullscreenActivity extends Activity implements OnClickListener, AnimationListener{
/**
* Whether or not the system UI should be auto-hidden after
* {#link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
*/
private static final boolean AUTO_HIDE = true;
private HeavyLifter chuckNorris;
private Animation animation1;
private Animation animation2;
private boolean isBackOfCardShowing = true;
/*
an array with the ids of the images for the viewpager
*/
public Integer[] mImageIds = {
R.drawable.background1, R.drawable.background2,
R.drawable.background3, R.drawable.background4,
R.drawable.background1, R.drawable.background2,
R.drawable.background1, R.drawable.background2,
R.drawable.background3, R.drawable.background4,
R.drawable.background1, R.drawable.background2,
R.drawable.background3
};
/*
an array with the ids of the images for the back flip of each image in the viewpager
*/
public Integer[] mBackIds = {
R.drawable.splash, R.drawable.splash,
R.drawable.splash, R.drawable.splash,
R.drawable.splash, R.drawable.splash,
R.drawable.splash, R.drawable.splash,
R.drawable.splash, R.drawable.splash,
R.drawable.splash, R.drawable.splash,
R.drawable.splash
};
/**
* If {#link #AUTO_HIDE} is set, the number of milliseconds to wait after
* user interaction before hiding the system UI.
*/
private static final int AUTO_HIDE_DELAY_MILLIS = 3000;
/**
* If set, will toggle the system UI visibility upon interaction. Otherwise,
* will show the system UI visibility upon interaction.
*/
private static final boolean TOGGLE_ON_CLICK = true;
/**
* The flags to pass to {#link SystemUiHider#getInstance}.
*/
private static final int HIDER_FLAGS = SystemUiHider.FLAG_HIDE_NAVIGATION;
/**
* The instance of the {#link SystemUiHider} for this activity.
*/
private SystemUiHider mSystemUiHider;
/**
* The pager widget, which handles animation and allows swiping horizontally to access previous
* and next wizard steps.
*/
private ViewPager mPager;
/**
* The pager adapter, which provides the pages to the view pager widget.
*/
private PagerAdapter mPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fullscreen);
animation1 = AnimationUtils.loadAnimation(this, R.anim.to_middle);
animation1.setAnimationListener(this);
animation2 = AnimationUtils.loadAnimation(this, R.anim.from_middle);
animation2.setAnimationListener(this);
findViewById(R.id.flip).setOnClickListener(this);
// Loop through the ids to create a list of full screen image views
ImageAdapter imageAdapter = new ImageAdapter(this);
List<ImageView> images = new ArrayList<ImageView>();
for (int i = 0; i < imageAdapter.getCount(); i++) {
ImageView imageView = new ImageView(this);
imageView.setId(i);
imageView.setImageResource(imageAdapter.mThumbIds[i]);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
images.add(imageView);
}
final View controlsView = findViewById(R.id.fullscreen_content_controls);
final View contentView = findViewById(R.id.view_pager);
// Set up an instance of SystemUiHider to control the system UI for
// this activity.
mSystemUiHider = SystemUiHider.getInstance(this, contentView, HIDER_FLAGS);
mSystemUiHider.setup();
mSystemUiHider.setOnVisibilityChangeListener(new SystemUiHider.OnVisibilityChangeListener() {
// Cached values.
int mControlsHeight;
int mShortAnimTime;
#Override
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public void onVisibilityChange(boolean visible) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
// If the ViewPropertyAnimator API is available
// (Honeycomb MR2 and later), use it to animate the
// in-layout UI controls at the bottom of the
// screen.
if (mControlsHeight == 0) {
mControlsHeight = controlsView.getHeight();
}
if (mShortAnimTime == 0) {
mShortAnimTime = getResources().getInteger(
android.R.integer.config_shortAnimTime);
}
controlsView
.animate()
.translationY(visible ? 0 : mControlsHeight)
.setDuration(mShortAnimTime);
} else {
// If the ViewPropertyAnimator APIs aren't
// available, simply show or hide the in-layout UI
// controls.
controlsView.setVisibility(visible ? View.VISIBLE
: View.GONE);
}
if (visible && AUTO_HIDE) {
// Schedule a hide().
delayedHide(AUTO_HIDE_DELAY_MILLIS);
}
}
});
// Set up the user interaction to manually show or hide the system UI.
contentView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (TOGGLE_ON_CLICK) {
mSystemUiHider.toggle();
} else {
mSystemUiHider.show();
}
}
});
// Upon interacting with UI controls, delay any scheduled hide()
// operations to prevent the jarring behavior of controls going away
// while interacting with the UI.
findViewById(R.id.btnSetAsWallpaper).setOnTouchListener(
mDelayHideTouchListener);
findViewById(R.id.btnSaveWallpaper).setOnTouchListener(
mDelayHideTouchListener);
// Finally create the adapter
ImagePagerAdapter imagePagerAdapter = new ImagePagerAdapter(images);
ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
viewPager.setAdapter(imagePagerAdapter);
// Set the ViewPager to point to the selected image from the previous activity
// Selected image id
int position = getIntent().getExtras().getInt("id");
viewPager.setCurrentItem(position);
//set title of Image
this.setTitle(imageAdapter.mThumbTitles[position]);
// Load are heavy lifter (goes and does work on another thread), to get a response after the lifters thread
// has finished we pass in a Handler that will be notified when it completes
chuckNorris = new HeavyLifter(this, chuckFinishedHandler);
}
#Override
public void onClick(View v) {
v.setEnabled(false);
((ImageView)findViewById(getIntent().getExtras().getInt("id"))).clearAnimation();
((ImageView)findViewById(getIntent().getExtras().getInt("id"))).setAnimation(animation1);
((ImageView)findViewById(getIntent().getExtras().getInt("id"))).startAnimation(animation1);
}
#Override
public void onAnimationEnd(Animation animation) {
int i = getIntent().getExtras().getInt("id");
if (animation==animation1) {
if (isBackOfCardShowing) {
((ImageView)findViewById(i)).setImageResource(mImageIds[i]);
} else {
((ImageView)findViewById(i)).setImageResource(mBackIds[i]);
}
((ImageView)findViewById(getIntent().getExtras().getInt("id"))).clearAnimation();
((ImageView)findViewById(getIntent().getExtras().getInt("id"))).setAnimation(animation2);
((ImageView)findViewById(getIntent().getExtras().getInt("id"))).startAnimation(animation2);
} else {
isBackOfCardShowing=!isBackOfCardShowing;
findViewById(R.id.flip).setEnabled(true);
}
}
#Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
delayedHide(100);
}
/**
* Touch listener to use for in-layout UI controls to delay hiding the
* system UI. This is to prevent the jarring behavior of controls going away
* while interacting with activity UI.
*/
View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (AUTO_HIDE) {
delayedHide(AUTO_HIDE_DELAY_MILLIS);
}
return false;
}
};
Handler mHideHandler = new Handler();
Runnable mHideRunnable = new Runnable() {
#Override
public void run() {
mSystemUiHider.hide();
}
};
/**
* Schedules a call to hide() in [delay] milliseconds, canceling any
* previously scheduled calls.
*/
private void delayedHide(int delayMillis) {
mHideHandler.removeCallbacks(mHideRunnable);
mHideHandler.postDelayed(mHideRunnable, delayMillis);
}
/**
* Called from XML when the save wallpaper button is pressed
* Thie retrieves the id of the current image from our list
* It then asks chuck to save it as a wallpaper!
* The chuckHandler will be called when this operation is complete
* #param v
*/
public void saveWallpaper(View v) {
// get intent data
Intent i = getIntent();
// Selected image id
int position = i.getExtras().getInt("id");
ImageAdapter imageAdapter = new ImageAdapter(this);
// Show a toast message on successful save
if(chuckNorris.saveResourceAsWallpaper(imageAdapter.mThumbIds[position])){
Toast.makeText(FullscreenActivity.this, "Image Saved to ZamaaanWallpaper folder", Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(FullscreenActivity.this, "Image couldn't be saved, try again", Toast.LENGTH_SHORT).show();
}
}
/**
* Called from XML when the set wallpaper button is pressed
* Thie retrieves the id of the current image from our list
* It then asks chuck to set it as a wallpaper!
* The chuckHandler will be called when this operation is complete
* #param v
*/
public void setAsWallpaper(View v) {
// get intent data
Intent i = getIntent();
// Selected image id
int position = i.getExtras().getInt("id");
ImageAdapter imageAdapter = new ImageAdapter(this);
chuckNorris.setResourceAsWallpaper(imageAdapter.mThumbIds[position]);
}
/**
* This is the handler that is notified when are HeavyLifter is finished doing an operation
*/
private Handler chuckFinishedHandler = new Handler(){
#Override
public void handleMessage(Message msg) {
switch(msg.what){
case SUCCESS:
Toast.makeText(FullscreenActivity.this, "Wallpaper set", Toast.LENGTH_SHORT).show();
break;
case FAIL:
Toast.makeText(FullscreenActivity.this, "Wallper NOT set, try again", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
};
}
I fixed my issues. I simply had to :
Change the boolean isBackOfCardShowing to an array of boolean for each page and intialize it to false.
Use viewpager.getCurrentItem() to get the position of the current page instead of what I was doing.

Create a message dialog in Android via NDK callback

I have an android app in which I've set up an OpenGL context on the Java side, and am sending drawing from the NDK/C++ side. This all seems to work well.
I want the C++ side to be able to pop up a dialog. I implemented a java MakeADialog function which is getting fired just fine from the C side via a env->CallVoidMethod(javaClass, javaMethod); My Java-side receiving function looks like this:
public static void MakeADialog() {
Log.w("title", "MakeADialog fired!");
}
This is in a separate class (not an Activity or Runnable). The above works fine and I can see my MakeADialg log message. However, when I try to create an actual dialog box, then I get lots of crashes. I can tell I'm not grocking what 'thread' I'm running on when I call from the C side into the Java side. It appears I'm getting into trouble when I try to create a new thread/dialog.
I've tried lots of the suggestions on here about creating a Runnable, Thread, etc - but they always seem to give me the dreaded 'Can't create handler inside thread that has not called Looper.prepare()' or that the parent is null for the view. Most of these methods revolve around storing the Activity and Context pointers as static and retrieving them via get functions when I'm in the MakeADialog callback.
AlertDialog alertDialog = new AlertDialog.Builder(MyApp.GetMyContext()).create(); where the GetMyContext() function simply returns the this pointer of the main activity creation thread that I stored during app startup.
Has anyone popped up a dialog launched from their NDK side or can point me to some relevant docs that will help me understand how to create a new dialog from an NDK callback?
Thanks in advance!
Maybe we can use the example from gist to create a modal dialog. I suspected that You called it from a worker thread so "they always seem to give me the dreaded 'Can't create handler inside thread that has not called Looper.prepare()' or that the parent is null for the view." (see also Can't create handler inside thread that has not called Looper.prepare()).
The key code based on the official example Native Activity and the gist code:
On the Java side,
package ss.fang.brickgo;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.NativeActivity;
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
public class GameActivity extends NativeActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
/**
* This function will be called from C++ by name and signature (Ljava/lang/String;Z)I)
*
* #param message the message text to show
* #param model if true, it will block the current thread, otherwise, it acts like a modeless dialog.
* #return return id of the button that was clicked for a model dialog, otherwise, 0.
* #see #showAlertCallback
* #see <a href="https://stackoverflow.com/questions/11730001/create-a-message-dialog-in-android-via-ndk-callback/60611870#60611870">
* Create a message dialog in Android via NDK callback</a>
* #see <a href="https://stackoverflow.com/questions/6120567/android-how-to-get-a-modal-dialog-or-similar-modal-behavior">
* Android: How to get a modal dialog or similar modal behavior?</a>
*/
public int showAlert(final String message, boolean model) {
//https://stackoverflow.com/questions/11411022/how-to-check-if-current-thread-is-not-main-thread
if (Looper.myLooper() == Looper.getMainLooper() && model) {
// Current Thread is UI Thread. Looper.getMainLooper().isCurrentThread()
//android.os.NetworkOnMainThreadException
throw new RuntimeException("Can't create a model dialog inside Main thread");
}
ApplicationInfo applicationInfo = getApplicationInfo();
final CharSequence appName = getPackageManager().getApplicationLabel(applicationInfo);
// Use a semaphore to create a modal dialog. Also, it's holden by the dialog's listener.
final Semaphore semaphore = model ? new Semaphore(0, true) : null;
// The button that was clicked (ex. BUTTON_POSITIVE) or the position of the item clicked
final AtomicInteger buttonId = new AtomicInteger();
this.runOnUiThread(new Runnable() {
public void run() {
AlertDialog.Builder builder = new AlertDialog.Builder(GameActivity.this, AlertDialog.THEME_HOLO_DARK);
builder.setTitle(appName);
builder.setMessage(message);
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
buttonId.set(id);
if (null != semaphore)
semaphore.release();
else
showAlertCallback(id);
if (DialogInterface.BUTTON_POSITIVE == id) {
GameActivity.this.finish();
}
}
};
builder.setNegativeButton(android.R.string.cancel, listener);
builder.setPositiveButton(android.R.string.ok, listener);
builder.setCancelable(false);
AlertDialog dialog = builder.create();
dialog.show();
}
});
if (null != semaphore)
try {
semaphore.acquire();
} catch (InterruptedException e) {
Log.v("GameActivity", "ignored", e);
}
return buttonId.get();
}
/**
* The callback for showAlert when it acts like a modeless dialog
*
* #param id the button that was clicked
*/
public native void showAlertCallback(int id);
/**
* #see <a href="https://stackoverflow.com/questions/13822842/dialogfragment-with-clear-background-not-dimmed">
* DialogFragment with clear background (not dimmed)</a>
*/
protected void showDialog() {
Dialog dialog = new Dialog(this);
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
// layout to display
dialog.setContentView(R.layout.dialog_layout);
// set color transpartent
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.show();
}
}
On the Native (C++11) side,
/** #param onClickListener MUST be NULL because the model dialog is not implemented. */
typedef void *(OnClickListener)(int id);
jint showAlert(struct android_app *state, const char *message, bool model = false);
/** Process the next input event. */
static int32_t engine_handle_input(struct android_app *app, AInputEvent *event) {
auto *engine = (struct engine *) app->userData;
auto type = AInputEvent_getType(event);
if (AINPUT_EVENT_TYPE_MOTION == type) {
engine->animating = 1;
engine->state.x = AMotionEvent_getX(event, 0);
engine->state.y = AMotionEvent_getY(event, 0);
return 1;
} else if (AINPUT_EVENT_TYPE_KEY == type) {
auto action = AKeyEvent_getAction(event);
if (AKEY_EVENT_ACTION_DOWN == action && AKEYCODE_BACK == AKeyEvent_getKeyCode(event)) {
//https://stackoverflow.com/questions/15913080/crash-when-closing-soft-keyboard-while-using-native-activity
// skip predispatch (all it does is send to the IME)
//if (!AInputQueue_preDispatchEvent(app->inputQueue, event))
//int32_t handled = 0;
//if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
//AInputQueue_finishEvent(app->inputQueue, event, handled);
LOGI("Before showAlert in modal behavior, it blocks until the dialog dismisses.");
showAlert(app, "Go Back?", true);
LOGI("After showAlert in modal behavior, the dialog should have been dismissed.");
return 1;
}
}
return 0; //not handled
}
/** #return the id of the button clicked if model is true, or 0 */
jint showAlert(struct android_app *state, const char *message, bool model /* = false */) {
JNIEnv *jni = NULL;
state->activity->vm->AttachCurrentThread(&jni, NULL);
jclass clazz = jni->GetObjectClass(state->activity->clazz);
// Get the ID of the method we want to call
// This must match the name and signature from the Java side Signature has to match java
// implementation (second string hints a java string parameter)
jmethodID methodID = jni->GetMethodID(clazz, "showAlert", "(Ljava/lang/String;Z)I");
// Strings passed to the function need to be converted to a java string object
jstring jmessage = jni->NewStringUTF(message);
jint result = jni->CallIntMethod(state->activity->clazz, methodID, jmessage, model);
// Remember to clean up passed values
jni->DeleteLocalRef(jmessage);
state->activity->vm->DetachCurrentThread();
return result;
}
extern "C"
JNIEXPORT void JNICALL
Java_ss_fang_brickgo_GameActivity_showAlertCallback(JNIEnv *env, jobject thiz, jint id) {
LOGI("showAlertCallback %d", id);
}

Categories