I'm trying to use SharedPreferencesBackupHelper to save my SharedPreferences value to cloud.
AndroidManifest.xml
<application
android:allowBackup="true"
android:backupAgent=".DataBackupAgent"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<meta-data android:name="com.google.android.backup.api_key" android:value="AEdPqrEAAAAIXMH86OqosQlXYuS0QbfyOaZT8fUadY1QUDzo2w" />
<activity
android:name=".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>
DataBackupAgent.java:
public class DataBackupAgent extends BackupAgentHelper {
public static final String PREFS = "data_prefs";
public static final String PREFS_BACKUP_KEY = "myprefs";
#Override
public void onCreate() {
SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
}
}
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
backupManager = new BackupManager(this);
prefs = getSharedPreferences(DataBackupAgent.PREFS, Context.MODE_PRIVATE);
edit = prefs.edit();
text = (EditText)findViewById(R.id.editText);
String value = prefs.getString(DataBackupAgent.PREFS_BACKUP_KEY,"");
text.setText(value);
Button btnBackup = (Button)findViewById(R.id.button);
btnBackup.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
edit.putString(DataBackupAgent.PREFS_BACKUP_KEY,text.getText().toString());
edit.commit();
backupManager.dataChanged();
}
});
}
My steps:
Write something at the EditText, click Backup button
Close app and open again. The saved value will be shown in EditText
Uninstall the app and reinstall again. The saved value is not shown in EditText at all.
Edit at 27/02/2015:
I added the following code to restore manually:
backupManager.requestRestore(new RestoreObserver() {
#Override
public void restoreFinished(int error) {
super.restoreFinished(error);
String value = prefs.getString(DataBackupAgent.PREFS_BACKUP_KEY,"");
text.setText(value);
}
#Override
public void restoreStarting(int numPackages) {
super.restoreStarting(numPackages);
}
#Override
public void onUpdate(int nowBeingRestored, String currentPackage) {
super.onUpdate(nowBeingRestored, currentPackage);
}
});
Unfortunately no callback functions are called.
This means back or auto restore doesn't work at all. Any idea? Thanks
My steps:
1. Write something at the EditText, click Backup button
2. Close app and open again. The saved value will be shown in EditText
3. Uninstall the app and reinstall again. The saved value is not shown in EditText at all.
To test your implementation there are others steps related to the use of bmgras we can see here.
Nevertheless I implemented this feature some days ago and following the steps in the documentation using a real device - Samsung SII - the automatic restore doesn't happen BUT using the emulator all was fine.
Logcat will show you all the operation output details.
IMO, the Android Data Backup feature is not reliable today. We can see some discussion about the implementation problems here and here.
Hope it helps!
i am trying to switch from one activity to other using intents, first activity is running properly but when second activity starts the app gets stopped. i have attached the code with this.
Manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sampleapp"
android:versionCode="1"
android:versionName="1.0" >
...
<application
...
<activity
android:name="com.example.sampleapp.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>
<activity
android:name="com.example.sampleapp.welwithname"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.WELWITHNAME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
...
</application>
</manifest>
this is the default activity. i tried to get the string and send it to the next activity.here is the xml and activity.
Activity Code:
public class MainActivity extends Activity {
public final static String EXTRA_MESSAGE = "com.example.sampleapp.MESSAGE";
String tempname;
Button submit;
EditText name;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
submit= (Button)findViewById(R.id.button1);
submit.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,Welwithname.class);
EditText editText = (EditText) findViewById(R.id.getname);
String message = editText.getText().toString();
intent.putExtra(EXTRA_MESSAGE, message);
startActivity(intent);
}
});
}
}
Second Activity:
this activity gets the string from activity 1 and print the value..heres the xml and activity
Activity Code:
public class Welwithname extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.welwithname);
Intent intent = getIntent();
String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
// Create the text view
TextView textView = (TextView)findViewById(R.id.textView1);
textView.setTextSize(40);
textView.setText("Welcome" + message + "!!!" );
Button sq = (Button)findViewById(R.id.sq);
Button exit = (Button)findViewById(R.id.exit);
sq.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View V)
{
Intent main = new Intent(Welwithname.this,MainScreen.class);
startActivity(main);
}
});
// Set the text view as the activity layout
setContentView(textView);
}
...
}
what changes should i made in this code to make it run?
<activity
android:name="com.example.sampleapp.welwithname"
Isn't your class called Welwithname ?
capitalise the 'W' in the Activity name in your AndroidManifest.xml
You are not going to be a developer long if you do not read the console / logcat output when you come across a crash.
If you read your LogCat it is most likely saying "Cannot find Activity welwithname, have you declared it in your Manifest?" and that would be your answer and StackOverflow would be a happier place
http://developer.android.com/tools/help/logcat.html
I'm using this code to jump back in activity stack (mainly to move to home Activity):
Intent goTo = new Intent(this, HomeActivity.class);
goTo.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(goTo);
So I create new Intent and set "target" to HomeActivity which is in Activity stack so whole stack will be cleared from top to this HomeActivity.
Now I need slightly different use case. I have for example five Activities A-B-C-D-E (A started B etc.) on the stack. Now I need to jump from E to C or B depending of what user choose. The problem is that Activities A, B, C, D, E have same class. So I can't use example above because I don't know how to target that Activity.
So the question is if there is any way how to "tag activity" or manipulate with stack.
Thanks!
I haven't tried it myself, but I think the best option would be to refactor your app to use a stack of Fragments within a single Activity (since you can then more easily manage the backstack using the provided addToBackStack() and popBackStack() methods). Basically this involves moving most of the code in your Activity into a Fragment and then adding the backstack manipulation code in the Activity). You can look at the code for FragmentBreadCrumbs (with API 11+) or the code for HanselAndGretel (for use with the compatibility library) to see how this can be implemented.
However, if you want to continue using your current multi-Activity approach, the following is some code I came up with to illustrate how you can do this.
First, add several internal classes to alias your current Activity and put these classes into a sequence list (note also the simplistic getSequencedActivityIntent() method that I wrote, you can add more advanced logic if you need - maybe use a HashMap to associate each class in the sequence with an arbitrary tag value?):
public class MyActivity extends Activity {
public static class A extends MyActivity {}
public static class B extends MyActivity {}
public static class C extends MyActivity {}
public static class D extends MyActivity {}
public static class E extends MyActivity {}
public static class F extends MyActivity {}
public static class G extends MyActivity {}
public static class H extends MyActivity {}
public static class I extends MyActivity {}
public static class J extends MyActivity {}
private final static List<Class<?>> SEQUENCE = Arrays.asList(new Class<?>[] {
A.class, B.class, C.class, D.class, E.class,
F.class, G.class, H.class, I.class, J.class,
});
private Intent getSequencedActivityIntent(int step) {
final int current = SEQUENCE.indexOf(this.getClass());
if (current == -1) new Intent(this, SEQUENCE.get(0));
final int target = current + step;
if (target < 0 || target > SEQUENCE.size() - 1) return null;
return new Intent(this, SEQUENCE.get(target));
}
// the rest of your activity code
}
Don't forget to add their entries to your AndroidManifest.xml file too (singleTop is optional - it will prevent the Activity instance in the stack to be created again when brought back to the front):
<activity android:name=".MyActivity$A" android:launchMode="singleTop" />
<activity android:name=".MyActivity$B" android:launchMode="singleTop" />
<activity android:name=".MyActivity$C" android:launchMode="singleTop" />
<activity android:name=".MyActivity$D" android:launchMode="singleTop" />
<activity android:name=".MyActivity$E" android:launchMode="singleTop" />
<activity android:name=".MyActivity$F" android:launchMode="singleTop" />
<activity android:name=".MyActivity$G" android:launchMode="singleTop" />
<activity android:name=".MyActivity$H" android:launchMode="singleTop" />
<activity android:name=".MyActivity$I" android:launchMode="singleTop" />
<activity android:name=".MyActivity$J" android:launchMode="singleTop" />
Now, whenever you need to start a new "top" instance of your Activity, you can do something like:
final Intent intent = getSequencedActivityIntent(+1);
if (intent == null) return;
intent.putExtra("dataset", dataSet);
startActivity(intent);
And when you need to go back to one of the instance in the backstack you can do:
final Intent intent = getSequencedActivityIntent(- stepBack);
if (intent == null) return;
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
You can index your activities without having to worry about handling all the chain of onActivityResults using a super Activity that you extend in all your activities
Here is an implementation (I did not test it) but if you extend this SuperActivity in all your Activities, you can call fallBackToActivity( int ) to any activity using its index and each activity now has a getIndex(). You can use it to fallback to a relative index like getIndex()-3
package sherif.android.stack.overflow;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class SuperActivity extends Activity {
private static String EXTRA_INDEX = "SUPER_INDEX";
private static int RESULT_FALLBACK = 0x123456;
private int index;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getIntent()!=null) {
index = getIntent().getIntExtra(EXTRA_INDEX, -1) + 1;
}
}
protected final int getIndex() {
return index;
}
protected final void fallBackToActivity(int index) {
Intent intent = new Intent();
intent.putExtra(EXTRA_INDEX, index);
setResult(RESULT_FALLBACK, intent);
finish();
}
#Override
public void startActivityForResult(Intent intent, int requestCode) {
intent.putExtra(EXTRA_INDEX, getIndex());
super.startActivityForResult(intent, requestCode);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_FALLBACK) {
if(data.getIntExtra(EXTRA_INDEX, -1)!=getIndex()) {
setResult(RESULT_FALLBACK, data);
finish();
}
}
}
}
You can just keep a condition in your statement
if user chooses this item pass intent to B class
and if user chooses that item pass intent to C class
Add extra to your intent that will point the activity what to do.
For example
intent.putExtra("STATE", 1);
And get this value in onCreate of your activity.
getIntent().getExtras()
As I understand Android only targets the class of activity and not a particular instance of activity. So, I think you won't be able to do what you want by just adding some flags on Intent.
I think the easiest approach would be to implement it on your own by something like this
a) Create some singleton and have a member in it which points to instance of activity to which you want to return (as example activity B). Probably, you will have to store all activities in some list to be able to get instance of some previously launched activity.
b) Override onResume for all activities and in it do following check:
if (SomeSingleton.getTargetActivity() != null && this != SomeSingleton.getTargetActivity())
finish();
else
SomeSingleton.setTargetActivity(null);
c) As soon as you need to return from E do
SomeSingleton.setTargetActivity(B);
finish();
This will close top activity (which is E) and call onResume on activity D. It will check whether it's target. If it's not then it will close it and system will call onResume on activity C and so on.
If you want to use an abnormal approach, or some tricky once, you will have more problems later. I think you can
Define a abstract/non-abstract subclass of Activity and define everything you want. If other classes are exactly the same as the above class, so just subclass from it and do nothing more. But if the classes ( Activities ) may different from each other, you can provide abstract/non-abstract methods to define additional abilities.
So
You write a reusable codes for all activities,
You act normal so you will get good result
You can control everything specialized in your activites
You can control stack using manifest file
and more
for detailed information see below codes:
Parent Activity:
public abstract class AbstractActivity extends Activity {
AbstractActivity currentActivity;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
currentActivity = this;
someProtectedMethod();
commonMethod();
// etc...
/* event handling */
Button btn_first = (Button) findViewById(R.id.btn_first);
Button btn_second = (Button) findViewById(R.id.btn_second);
btn_first.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(currentActivity, FirstActivity.class);
currentActivity.startActivity(intent);
}
});
btn_second.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(currentActivity, SecondActivity.class);
currentActivity.startActivity(intent);
}
});
}
/** you must override it, so you can control specified things safe */
protected abstract void someProtectedMethod();
protected void commonMethod() {
Log.i("LOG", "Hello From " + getClass().getName());
}
#Override
protected void onResume() {
super.onResume();
//some statement that work in all activities to
Log.i("LOG", "On Resume: " + getClass().getName());
}
}
First Activity:
public class FirstActivity extends AbstractActivity {
#Override
protected void someProtectedMethod() {
Log.i("LOG", "Special Action From First Activity");
}
}
Second Activity:
public class SecondActivity extends AbstractActivity {
#Override
protected void someProtectedMethod() {
Log.i("LOG", "Special Action From Second Activity");
}
}
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<Button
android:id="#+id/btn_first"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="Open First Activity" />
<Button
android:id="#+id/btn_second"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="Open Second Activity" />
</LinearLayout>
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activity_control"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="7" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name=".FirstActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:label="#string/app_name" >
</activity>
</application>
</manifest>
The best and the easiest solution (so far) will be use Fragments and FragmentManager. Then tag each Fragment and use FragmentManager. Using only Activity can be very difficult to have almost the same result.
When I launch my app from Eclipse to my phone, it launches the Launcher first and then my main activity. It successfully passes a variable to the main activity. I check that the username has been logged with a toast.
When I launch my app from my phone directly, it goes straight to the main activity; the main activity registers the variable as null.
I created a test app that performs EXACTLY the same function as the launcher in this one; their manifests are identical except for the activity names; and that test app functions correclty from the phone and when I install it from Eclipse.
this is a real brain-teaser.
Here is the code for the starter activity:
public class SecureAppStarter extends Activity {
TextView report;
WebView input;
Context thisContext = this;
String thisValue, url;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.starter);
initialize();
WebViewClient rclient = new WebViewClient(){
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url){
return false;
}
#Override
public void onLoadResource(WebView view, String url){
input.getSettings().setJavaScriptEnabled(true);
input.addJavascriptInterface(new CustomJavaScriptInterface(thisContext), "Android");
}
#Override
public void onPageFinished(WebView view, String url){
report.setText(""+thisValue);
if (thisValue!= null){
Intent passOn = new Intent("arbuckle.app.MainActivity");
passOn.putExtra("username", thisValue);
startActivity(passOn);
}
}
};
rclient.onPageFinished(input, url);
input.setWebViewClient(rclient);
input.loadUrl(url);
}
public class CustomJavaScriptInterface {
Context mContext;
CustomJavaScriptInterface(Context context) {
mContext = context;
}
public void getValue(String value){
thisValue = value;
}
}
private void initialize() {
report = (TextView) findViewById(R.id.tvViewName);
input = (WebView) findViewById(R.id.wbWebAuth);
url = "http://URL.of.data.com";
}
}
And here is the manifest:
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name="SecureAppStarter"
android:label="#string/app_name"
android:configChanges="keyboardHidden|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ArbuckleAppActivity"
android:label="#string/app_name"
android:configChanges="keyboardHidden|orientation">
<intent-filter>
<action android:name="arbuckle.app.ArbuckleAppActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
Well I figured it out.
I have been installing/uninstalling versions of the app for weeks where the main activity was the launcher. clearly that gets kept somewhere in Android cache and it was choosing the main activity to launch even though it wasn't the launcher anymore.
So I uninstalled the app completely; and reinstalled. Now it works.
Does anyone think I hacked at the problem instead of solving it? Should I expect further problems down the road?
I am new to RoboGuice and I am trying to set up my activity to use DI. However, nothing happens when I attempt to use it. I only get a blank black window with no content and no logging in my Activity.onCreate() method after I call "super.onCreate(savedInstanceState);"
See these 2 snippets of code:
public class ClikClokActivity extends RoboActivity{
#Inject
private TileAdapter tileAdapter;
#Inject
private GameLogicService gameLogicService;
#Inject
private GridOperationQueue gridOperationQueue;
private GridView gridView;
#Inject
private Handler handler;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
Log.v(this.getClass().toString(), "Entering onCreate");
super.onCreate(savedInstanceState);
Log.v(this.getClass().toString(), "Never logs this with RoboGuice");
setContentView(R.layout.main);
gridView = (GridView) findViewById(R.id.gridview);
gridView.setNumColumns(Constants.GRID_WIDTH);
gridView.setAdapter(tileAdapter);
Log.v(this.getClass().toString(), "GridView initialized");
gridOperationQueue.start();
Log.v(this.getClass().toString(), "Completed onCreate");
}
and
public class ClikClokApplication extends RoboApplication{
#Override
protected void addApplicationModules(List<Module> modules) {
modules.add(new ClikClokModule());
}
}
and
public class ClikClokModule extends AbstractAndroidModule {
#Override
protected void configure() {
}
}
and
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.clikclok"
android:versionCode="1"
android:versionName="1.0">
<application android:name="com.clikclok.ClikClokApplication" android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".ClikClokActivity"
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>
If you look at the above code, I never get the second logging. However, if I was to extend from Activity instead and remove the android:name="com.clikclok.ClikClokApplication" attribute from my manifest then I do get the second logging (albeit fails with NullPointers as there is no initialization performed).
So what may be happening in super.onCreate(savedInstanceState); that is causing my application not to work?
Thanks
Update from the above:
I've spent quite a bit of time investigating this and using Eclipse's debugger can now see where my code seems to hang within RoboGuice.
The following code is from the InjectorImpl class:
public void injectMembers(Object instance) {
// Reaches here but...
MembersInjector membersInjector = getMembersInjector(instance.getClass());
// ....this comment is never reached
membersInjector.injectMembers(instance);
}
So I dug into the Guice 3.0 code using my debugger and into the FailableCache class:
public V get(K key, Errors errors) throws ErrorsException {
// Reaches here....
Object resultOrError = delegate.get(key);
// ...but not here
if (resultOrError instanceof Errors) {
errors.merge((Errors) resultOrError);
throw errors.toException();
} else {
#SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
V result = (V) resultOrError;
return result;
}
}
How could this be that it just hangs while retrieving a key from a map? I'm not familiar enough with the code and it is quite confusing to troubleshoot.
Any advice is appreciated.
Try to add some bindings in your configure method.
You can also try to inject your grid view like this
#InjectView(R.id.gridview)
GridView gridView;
I hope this will help you.
Regards.
This was caused by me attempting to inject my Activity class into a service class.
So I was attempting to #Inject an instance of "ClikClokActivity" instead of "Activity".
Once I removed this injection attempt, everything worked fine. Not sure if this exposes some other issue with RoboGuice or Guice itself.