Air iOS SharedObject deleted after updating - ios

I'm using SharedObjects in my game to store the progress of the player in the game (level scores, unlocked levels, etc.).
Everything is working fine, but the problem is when I submitted an update of the game (with the same certificates and the same names of the .swf and .ipa files) when the game is updated the old SharedObject is deleted and this is very big problem for the game and for me.
Both versions of the game are made with Flash CS 6 and Air SDK 3.5.
Any idea why the SharedObject is deleted, how can I prevent that?

I'm assuming that the reason why SharedObject becomes overwritten is because it's bundled with the .ipa during conversion.
I understand that will not help your current situation with salvaging your SharedObject but you could try using flash.filesystem to read/write your data to a preference file instead of employing SharedObject in the future.
Below is my Archive class that I use with my own work, but I've never developed for iOS before so i'm not certain that it will function as it does on other deployment targets, although I believe it should.
Use Case:
//Imports
import com.mattie.data.Archive;
import com.mattie.events.ArchiveEvent;
//Constants
private static const PREF_CANVAS_VOLUME:String = "prefCanvasVolume";
private static const DEFAULT_VOLUME:Number = 0.5;
//Initialize Archive
private function initArchive():void
{
archive = new Archive();
archive.addEventListener(ArchiveEvent.LOAD, init);
archive.load();
}
//Initialize
private function init(event:ArchiveEvent):void
{
archive.removeEventListener(ArchiveEvent.LOAD, init);
canvasVolume = archive.read(PREF_CANVAS_VOLUME, DEFAULT_VOLUME);
}
//Application Exiting Event Handler
private function applicationExitingEventHandler(event:Event):void
{
stage.nativeWindow.removeEventListener(Event.CLOSING, applicationExitingEventHandler);
archive.write(PREF_CANVAS_VOLUME, canvas.volume);
archive.addEventListener(ArchiveEvent.SAVE, archiveSavedEventHandler);
archive.save();
}
//Archive Saved Event Handler
private function archiveSavedEventHandler(event:ArchiveEvent):void
{
archive.removeEventListener(ArchiveEvent.SAVE, archiveSavedEventHandler);
NativeApplication.nativeApplication.exit();
}
Archive Class
package com.mattie.data
{
//Imports
import com.mattie.events.ArchiveEvent;
import flash.data.EncryptedLocalStore;
import flash.desktop.NativeApplication;
import flash.events.EventDispatcher;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.net.registerClassAlias;
import flash.utils.ByteArray;
//Class
public final class Archive extends EventDispatcher
{
//Properties
private static var singleton:Archive;
//Variables
private var file:File;
private var data:Object;
//Constructor
public function Archive()
{
if (singleton)
{
throw new Error("Archive is a singleton that is only accessible via the \"archive\" public property.");
}
file = File.applicationStorageDirectory.resolvePath(NativeApplication.nativeApplication.applicationID + "Archive");
data = new Object();
registerClassAlias("Item", Item);
}
//Load
public function load():void
{
if (file.exists)
{
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.READ);
data = fileStream.readObject();
fileStream.close();
}
dispatchEvent(new ArchiveEvent(ArchiveEvent.LOAD));
}
//Read
public function read(key:String, defaultValue:* = null):*
{
var value:* = defaultValue;
if (data[key] != undefined)
{
var item:Item = Item(data[key]);
if (item.encrypted)
{
var bytes:ByteArray = EncryptedLocalStore.getItem(key);
if (bytes == null)
{
return value;
}
switch (item.value)
{
case "Boolean": value = bytes.readBoolean(); break;
case "int": value = bytes.readInt(); break;
case "uint": value = bytes.readUnsignedInt(); break;
case "Number": value = bytes.readDouble(); break;
case "ByteArray": bytes.readBytes(value = new ByteArray()); break;
default: value = bytes.readUTFBytes(bytes.length);
}
}
else
{
value = item.value;
}
}
return value;
}
//Write
public function write(key:String, value:*, encrypted:Boolean = false, autoSave:Boolean = false):void
{
var oldValue:* = read(key);
if (oldValue != value)
{
var item:Item = new Item();
item.encrypted = encrypted;
if (encrypted)
{
var constructorString:String = String(value.constructor);
constructorString = constructorString.substring(constructorString.lastIndexOf(" ") + 1, constructorString.length - 1);
item.value = constructorString;
var bytes:ByteArray = new ByteArray();
switch (value.constructor)
{
case Boolean: bytes.writeBoolean(value); break;
case int: bytes.writeInt(value); break;
case uint: bytes.writeUnsignedInt(value); break;
case Number: bytes.writeDouble(value); break;
case ByteArray: bytes.writeBytes(value); break;
default: bytes.writeUTFBytes(value);
}
EncryptedLocalStore.setItem(key, bytes);
}
else
{
item.value = value;
}
data[key] = item;
dispatchEvent(new ArchiveEvent(ArchiveEvent.WRITE, key, oldValue, value));
if (autoSave)
{
save();
}
}
}
//Remove
public function remove(key:String, autoSave:Boolean = false):void
{
if (data[key] != undefined)
{
var oldValue:* = read(key);
if (Item(data[key]).encrypted)
{
EncryptedLocalStore.removeItem(key);
}
delete data[key];
dispatchEvent(new ArchiveEvent(ArchiveEvent.DELETE, key, oldValue));
if (autoSave)
{
save();
}
}
}
//Contains
public function contains(key:String):Boolean
{
return (data[key] != undefined);
}
//Save
public function save():void
{
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeObject(data);
fileStream.close();
dispatchEvent(new ArchiveEvent(ArchiveEvent.SAVE));
}
//Get Singleton
public static function get archive():Archive
{
if (!singleton)
{
singleton = new Archive();
}
return singleton;
}
}
}
//Item
class Item
{
//Variables
public var value:*;
public var encrypted:Boolean = false;
}
Archive Event Class
package com.mattie.events
{
//Imports
import flash.events.Event;
//Class
public class ArchiveEvent extends Event
{
//Constants
public static const LOAD:String = "load";
public static const WRITE:String = "write";
public static const DELETE:String = "delete";
public static const SAVE:String = "save";
//Properties
public var key:String;
public var oldValue:*;
public var newValue:*;
//Constructor
public function ArchiveEvent(type:String, key:String = null, oldValue:* = null, newValue:* = null)
{
super(type);
this.key = key;
this.oldValue = oldValue;
this.newValue = newValue;
}
//Clone
public override function clone():Event
{
return new ArchiveEvent(type, key, oldValue, newValue);
}
//To String
public override function toString():String
{
return formatToString("ArchiveEvent", "type", "key", "oldValue", "newValue");
}
}
}

Related

How do I programmatically add records to an Umbraco v8 form?

I'm looking to add records to an Umbraco v8 form. I know I need the form guid. Is this how I'd do it? Something like this?
public void PostFormData()
{
Guid FormGuid = new Guid("8494a8f0-94da-490e-bd61-7e658c226142");
var form = _formService.Get(FormGuid);
//place for field data into fieldDic
var fieldDic = new Dictionary<Guid, RecordField>();
var firstName = form.AllFields.First(f => f.Alias == "firstName");
var firstNameRecord = new RecordField(firstName);
firstNameRecord.Values = new List<object>() { "Mad Max" };
fieldDic.Add(firstName.Id, firstNameRecord);
var record = new Record()
{
Created = DateTime.Now,
Form = form.Id,
RecordFields = fieldDic,
State = FormState.Submitted,
};
record.RecordData = record.GenerateRecordDataAsJson();
_recordStorage.InsertRecord(record, form);
}
Here's how I do it. Note, I'm hard-coding the Record.UmbracoPageId to -1 while you might want to actually pass in the correct page ID.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Forms.Core.Data.Storage;
using Umbraco.Forms.Core.Models;
using Umbraco.Forms.Core.Persistence.Dtos;
using Umbraco.Forms.Core.Services;
namespace myProject.Services
{
public class FormServiceComposer : IUserComposer
{
public void Compose(Composition composition)
{
composition.Register<IFormService, FormService>(Lifetime.Request);
}
}
public interface IFormService
{
void InsertFormData(Guid formGuid, object formModel, string ipAddress);
}
public class FormService : IFormService
{
private readonly ILogger _logger;
private readonly Umbraco.Forms.Core.Services.IFormService _formService;
private readonly IRecordStorage _recordStorage;
private readonly IRecordFieldStorage _recordFieldStorage;
private readonly IWorkflowService _workflowService;
public FormService(ILogger logger, Umbraco.Forms.Core.Services.IFormService formService, IRecordStorage recordStorage, IRecordFieldStorage recordFieldStorage, IWorkflowService workflowService)
{
_logger = logger;
_formService = formService;
_recordStorage = recordStorage;
_recordFieldStorage = recordFieldStorage;
_workflowService = workflowService;
}
#region IFormService
public void InsertFormData(Guid formGuid, object formModel, string ipAddress)
{
try
{
Form form = _formService.GetForm(formGuid);
Record record = new Record();
foreach (Field field in form.AllFields)
{
string caption = CleanCaption(field.Caption);
if (formModel.GetType().GetProperty(caption) == null) continue;
var propertyValue = formModel.GetType().GetProperty(caption).GetValue(formModel, null);
if (propertyValue != null)
{
List<object> values = ExtractValues(propertyValue);
RecordField recordField = new RecordField
{
Alias = field.Alias,
FieldId = field.Id,
Field = field,
Key = Guid.NewGuid(),
Record = record.Id,
Values = values
};
_recordFieldStorage.InsertRecordField(recordField);
record.RecordFields.Add(recordField.Key, recordField);
}
}
record.Form = formGuid;
record.IP = ipAddress;
record.UmbracoPageId = -1;
record.State = Umbraco.Forms.Core.Enums.FormState.Approved;
record.RecordData = record.GenerateRecordDataAsJson();
_recordStorage.InsertRecord(record, form);
_recordStorage.DisposeIfDisposable();
}
catch (Exception ex)
{
_logger.Error<FormService>(ex, "Failed inserting Umbraco Forms data for {formGuid}");
}
}
#endregion IFormService
#region Private
private string CleanCaption(string caption)
{
Regex rgx = new Regex("[^a-zA-Z0-9 -]");
return rgx.Replace(caption.Trim().Replace(" ", ""), "");
}
private List<object> ExtractValues(object propertyValue)
{
List<object> result = new List<object>();
if (propertyValue is string == false && propertyValue.GetType().GetGenericTypeDefinition() == typeof(List<>))
{
IEnumerable<object> _propertyValue = (IEnumerable<object>)propertyValue;
if (_propertyValue.Any())
{
if (_propertyValue.First().GetType().GetProperties().Count() > 1)
{
JArray _properties = JArray.Parse(JsonConvert.SerializeObject(propertyValue));
foreach (JToken item in _properties)
{
string _value = string.Empty;
foreach (var _property in _propertyValue.First().GetType().GetProperties())
{
string _key = _property.Name;
_value = _value + (_value == "" ? "" : " - ") + item[_key].ToString();
}
result.Add(_value);
}
}
else
{
string _key = _propertyValue.First().GetType().GetProperties().First().Name;
JArray _properties = JArray.Parse(JsonConvert.SerializeObject(propertyValue));
foreach (JToken item in _properties)
{
result.Add(item[_key].ToString());
}
}
}
}
else
{
result.Add(propertyValue);
}
return result;
}
#endregion Private
}
}

pass initial data into a singleton factory in dart

I would like to pass some initial information into a singleton in dart.
Unfortunately, the information I like to access is null (see dartpad output below)
It seems like I get a new instance of my object and not the singleton but I can not wrap my head around it. Any idea?
ElmCommandProvider.fromMetaData
ElmCommandProvider._internal()
ElmCommandProvider._init
ElmCommandProvider()
null
This is the code which can be pasted in DartPad
class Command {
Command(this.i);
final int i;
}
class MetaData {
MetaData(this.i);
final int i;
}
class ElmCommandProvider {
List<Command> commandsList;
bool _lock = false;
static Map<String, MetaData> _metaDataPool;
factory ElmCommandProvider.fromMetaData(Map<String, MetaData> metaDataPool) {
print('ElmCommandProvider.fromMetaData');
assert(!_singleton._lock, "it's a singleton that can't re-defined");
ElmCommandProvider._metaDataPool = metaDataPool;
_singleton._lock = true;
ElmCommandProvider._init();
return _singleton;
}
factory ElmCommandProvider() {
print('ElmCommandProvider()');
return _singleton;
}
static final ElmCommandProvider _singleton =
new ElmCommandProvider._internal();
ElmCommandProvider._internal() {
print('ElmCommandProvider._internal()');
}
ElmCommandProvider._init() {
print('ElmCommandProvider._init');
commandsList =
_metaDataPool.values.map((bloc) => Command(bloc.i)).toList();
}
}
void main() {
ElmCommandProvider.fromMetaData({'1': MetaData(1), '2': MetaData(2)});
print( ElmCommandProvider().commandsList);
}
_init() should not be a constructor. Or at least there is no need for it to be one and it's confusing you. It should be changed to a static method or a private instance method.
When you do commandsList= in ElmCommandProvider._init(), commandsList is referring to the commandsList instance variable in the new ElmCommandProvider object you're creating with the constructor. You likely actually mean to modify the singleton's commandsList so you should have been doing singleton.commandsList = instead of just commandsList =.
Example working code with static method:
class Command {
Command(this.i);
final int i;
}
class MetaData {
MetaData(this.i);
final int i;
}
class ElmCommandProvider {
List<Command> commandsList;
bool _lock = false;
static Map<String, MetaData> _metaDataPool;
factory ElmCommandProvider.fromMetaData(Map<String, MetaData> metaDataPool) {
print('ElmCommandProvider.fromMetaData');
assert(!_singleton._lock, "it's a singleton that can't re-defined");
ElmCommandProvider._metaDataPool = metaDataPool;
_singleton._lock = true;
_init();
return _singleton;
}
factory ElmCommandProvider() {
print('ElmCommandProvider()');
return _singleton;
}
static final ElmCommandProvider _singleton =
new ElmCommandProvider._internal();
ElmCommandProvider._internal() {
print('ElmCommandProvider._internal()');
}
static _init() {
print('ElmCommandProvider._init');
_singleton.commandsList =
_metaDataPool.values.map((bloc) => Command(bloc.i)).toList();
}
}
void main() {
ElmCommandProvider.fromMetaData({'1': MetaData(1), '2': MetaData(2)});
print( ElmCommandProvider().commandsList);
}
Example working code with private instance method:
class Command {
Command(this.i);
final int i;
}
class MetaData {
MetaData(this.i);
final int i;
}
class ElmCommandProvider {
List<Command> commandsList;
bool _lock = false;
static Map<String, MetaData> _metaDataPool;
factory ElmCommandProvider.fromMetaData(Map<String, MetaData> metaDataPool) {
print('ElmCommandProvider.fromMetaData');
assert(!_singleton._lock, "it's a singleton that can't re-defined");
ElmCommandProvider._metaDataPool = metaDataPool;
_singleton._lock = true;
_singleton._init();
return _singleton;
}
factory ElmCommandProvider() {
print('ElmCommandProvider()');
return _singleton;
}
static final ElmCommandProvider _singleton =
new ElmCommandProvider._internal();
ElmCommandProvider._internal() {
print('ElmCommandProvider._internal()');
}
void _init() {
print('ElmCommandProvider._init');
commandsList =
_metaDataPool.values.map((bloc) => Command(bloc.i)).toList();
}
}
void main() {
ElmCommandProvider.fromMetaData({'1': MetaData(1), '2': MetaData(2)});
print( ElmCommandProvider().commandsList);
}

How to preserve insertion order in HashMap in Vala

I'm using a HashMap. When I iterate over the map, the data is returned in (often the same) random order. But the data was inserted in a specific order, and I need to preserve the insertion order. How can I do this in Vala? In Java there is LinkedHashMap but I don't see any equivalent for Gee.Map.
As far as I know, there is no equivalent of LinkedHashMap in Vala. Using a TreeMap and setting the comparison function to always return 1 (or -1 if you want the reverse order) for other Map entries will preserve the order and allow you to iterate through the Map in the order that items were added but get will not function as expected.
Unfortunately, after thoroughly examining the Gee source, there appears to be no way other than to roll your own. The most straightforward way is to subclass HashMap and use an ArrayList to keep a track of the order of the keys as they are inserted. You could also use a LinkedList, you would only need to change the internal ArrayList _keys field to a LinkedList. The choice depends on your use case. From the docs -
This implementation (ArrayList) is pretty good for rarely modified data. Because they are stored in an array this structure does not fit for highly mutable data.
The following is a basic implementation, in Vala (arrayhashmap.vala):
using Gee;
public class ArrayHashMap<K,V> : HashMap<K,V> {
private weak Set<K> _keyset;
private weak Collection<V> _values;
private weak Set<Entry<K,V>> _entries;
internal ArrayList<K> _keys = new ArrayList<K>();
private class KeySet<K> : AbstractSet<K> {
private weak ArrayList<K> _keys;
public KeySet (ArrayList<K> keys) {
_keys = keys;
}
public override Iterator<K> iterator () {
return _keys.iterator();
}
public override int size {
get { return _keys.size; }
}
public override bool read_only {
get { return true; }
}
public override bool add (K key) {
assert_not_reached ();
}
public override void clear () {
assert_not_reached ();
}
public override bool remove (K key) {
assert_not_reached ();
}
public override bool contains (K key) {
return _keys.contains (key);
}
}
private class ValueCollection<K,V> : AbstractCollection<V> {
private weak ArrayHashMap<K,V> _map;
public ValueCollection (ArrayHashMap map) {
_map = map;
}
public override Iterator<V> iterator () {
return new ValueIterator<K,V> (_map);
}
public override int size {
get { return _map.size; }
}
public override bool read_only {
get { return true; }
}
public override bool add (V value) {
assert_not_reached ();
}
public override void clear () {
assert_not_reached ();
}
public override bool remove (V value) {
assert_not_reached ();
}
public override bool contains (V value) {
Iterator<V> it = iterator ();
while (it.next ()) {
if (_map.value_equal_func (it.get (), value)) {
return true;
}
}
return false;
}
}
private class ValueIterator<K,V> : Object, Traversable<V>, Iterator<V> {
protected weak ArrayHashMap<K,V> _map;
protected Iterator<K> _keys;
public ValueIterator (ArrayHashMap<K,V> map) {
_map = map;
_keys = map._keys.iterator();
}
public bool next () {
return _keys.next();
}
public bool has_next () {
return _keys.has_next();
}
public virtual bool read_only {
get {
return true;
}
}
public bool valid {
get {
return _keys.valid;
}
}
public new V get () {
return _map.get(_keys.get());
}
public void remove () {
assert_not_reached ();
}
public bool foreach(ForallFunc<V> f) {
foreach (K key in _map._keys)
if (!f(_map.get(key)))
return false;
return true;
}
}
private class EntrySet<K,V> : AbstractSet<Entry<K, V>> {
private weak ArrayHashMap<K,V> _map;
public EntrySet (ArrayHashMap<K,V> map) {
_map = map;
}
public override Iterator<Entry<K, V>> iterator () {
return new EntryIterator<K,V> (_map);
}
public override int size {
get { return _map.size; }
}
public override bool read_only {
get { return true; }
}
public override bool add (Entry<K, V> entry) {
assert_not_reached ();
}
public override void clear () {
assert_not_reached ();
}
public override bool remove (Entry<K, V> entry) {
assert_not_reached ();
}
public override bool contains (Entry<K, V> entry) {
return _map.has (entry.key, entry.value);
}
}
private class EntryIterator<K,V> : Object, Traversable<Entry<K,V>>, Iterator<Entry<K,V>> {
protected weak ArrayHashMap<K,V> _map;
protected Iterator<K> _keys;
public EntryIterator (ArrayHashMap<K,V> map) {
_map = map;
_keys = map._keys.iterator();
}
public bool next () {
return _keys.next();
}
public bool has_next () {
return _keys.has_next();
}
public virtual bool read_only {
get {
return true;
}
}
public bool valid {
get {
return _keys.valid;
}
}
public new Entry<K,V> get () {
K* k = _keys.get();
var ent = new Entry<K,V>(k, _map.get(k));
return ent;
}
public void remove () {
assert_not_reached ();
}
public bool foreach(ForallFunc<Entry<K,V>> f) {
foreach (K key in _map._keys)
if (!f(new Entry<K,V>(key, _map.get(key))))
return false;
return true;
}
}
public class Entry<K,V> : Map.Entry<K,V> {
weak K _key;
weak V _value;
public override K key {
get {
return _key;
}
}
public override V value {
get {
return _value;
} set {
_value = value;
}
}
public override bool read_only {get { return true; }}
public Entry (K key, V value) {
this._key = key;
this._value = value;
}
}
public new void #set(K key, V value) {
if (!_keys.contains(key))
_keys.add(key);
base.set(key, value);
}
public new void unset(K key, out V? value = null) {
_keys.remove(key);
base.unset(key, out value);
}
public new void clear() {
base.clear();
_keys.clear();
}
public new Set<unowned K> keys {
owned get {
Set<K> keys = _keyset;
if (_keyset == null) {
keys = new KeySet<K> (_keys);
_keyset = keys;
keys.add_weak_pointer ((void**) (&_keyset));
}
return keys;
}
}
public new Collection<unowned V> values {
owned get {
Collection<K> values = _values;
if (_values == null) {
values = new ValueCollection<K,V> (this);
_values = values;
values.add_weak_pointer ((void**) (&_values));
}
return values;
}
}
public override Set<Entry<K,V>> entries {
owned get {
Set<Entry<K,V>> entries = _entries;
if (_entries == null) {
entries = new EntrySet<K,V> (this);
_entries = entries;
entries.add_weak_pointer ((void**) (&_entries));
}
return entries;
}
}
}
You can test it with this awful test case (tests.vala):
public static void doTest() {
const string[] strings = { "test", "another", "one-more", "how-about-this-one", "even-more" };
var entries3 = new ArrayHashMap<string, int>();
for (int i = 0; i < strings.length; i++)
entries3.set(strings[i], i);
entries3.unset("one-more");
foreach (var entry in entries3.keys)
message ("%s:%d", entry, entries3.get(entry));
entries3.set ("for-your-viewing-pleasure", 3);
foreach (var entry in entries3.keys)
message ("%s:%d", entry, entries3.get(entry));
entries3.set ("for-your-viewing-pleasure", 7);
foreach (var entry in entries3.entries)
message ("%s:%d", entry.key, entries3.get(entry.key));
}
public static int main (string[] args) {
Test.init(ref args);
Test.add_func ("/ArrayHashMap", doTest);
Test.run();
return 0;
}
Run the whole package together:
valac --pkg gee-0.8 -g tests.vala arrayhashmap.vala
This is a very rough implementation, based on how HashMap works internally. You may want to refactor it for better maintainability and write some more unit tests. If you find any problems, let me know and we can work through them.
I hope this helps.
Never heard of Vala, but it's easy to do (roughly) on your own what LinkedHashMap does internally. Write a wrapper that contains a doubly linked list of keys along with the hash map. Values in the map must consist of pairs, where one element is the actual map value and the other is a reference to the linked list node for the key. For each add, enqueue the key at the end of the list in addition to adding the key-><value, node ptr> entry to the map. For each remove, delete the associated key from the list using the node pointer (a constant time operation due to the double links), then remove the entry from the map. To look up a key, use the map. To traverse in insertion order, traverse the list.
Okay, since the originally accepted answer turned out to be incorrect, here's a quick and dirty working example in Java. I'll let you translate to Vala.
import java.util.HashMap;
import java.util.Iterator;
public class MyLinkedHashMap<K, V> implements Iterable<K> {
private final HashMap<K, Pair<K, V>> map = new HashMap<>();
private final Link<K> header = makeHeader();
/** Hash value along with a link reference to support remove(). */
private static class Pair<K, V> {
V value;
Link<K> link;
Pair(V value, Link<K> link) {
this.value = value;
this.link = link;
}
}
/** A link in the doubly linked list of keys. */
private static class Link<K> {
K key;
Link<K> prev;
Link<K> next;
Link() {}
Link(K key, Link<K> prev, Link<K> next) {
this.key = key;
this.prev = prev;
this.next = next;
}
}
#Override
public Iterator<K> iterator() {
return new MyLinkedHashMapIterator();
}
/** Iterator over map keys guaranteed to produce insertion order. */
private class MyLinkedHashMapIterator implements Iterator<K> {
private Link<K> ptr = header.next;
#Override
public boolean hasNext() {
return ptr != header;
}
#Override
public K next() {
K key = ptr.key;
ptr = ptr.next;
return key;
}
}
/** Make a header for a circular doubly linked list. */
private static <K> Link<K> makeHeader() {
Link<K> header = new Link<K>();
return header.next = header.prev = header;
}
/** Put a key/value in the map, remembering insertion order with a link in the list. */
public V put(K key, V value) {
Link<K> link = new Link<K>(key, header.prev, header);
link.prev.next = link;
header.prev = link;
Pair<K, V> pair = map.put(key, new Pair<>(value, link));
return pair == null ? null : pair.value;
}
/** Get the value mapped to a key or return {#code null} if none. */
public V get(K key) {
Pair<K, V> pair = map.get(key);
return pair == null ? null : pair.value;
}
/** Remove a key from both map and linked list. */
public V remove(K key) {
Pair<K, V> pair = map.remove(key);
if (pair == null) {
return null;
}
pair.link.prev.next = pair.link.next;
pair.link.next.prev = pair.link.prev;
return pair.value;
}
/** Trivial unit test. */
public static void main(String [] args) {
MyLinkedHashMap<String, Integer> map = new MyLinkedHashMap<>();
int n = 0;
for (String key : new String [] { "one", "two", "three", "four", "five", "six", "seven" }) {
map.put(key, ++n);
}
for (String key : map) {
System.out.println("For key " + key + " we have " + map.get(key));
}
String [] evenKeys = new String [] { "two", "four", "six" };
for (String evenKey : evenKeys) {
map.remove(evenKey);
}
System.out.println("After even keys removed...");
for (String key : map) {
System.out.println("For key " + key + " we have " + map.get(key));
}
n = 0;
for (String evenKey : evenKeys) {
map.put(evenKey, n += 2);
}
System.out.println("After putting them back again...");
for (String key : map) {
System.out.println("For key " + key + " we have " + map.get(key));
}
}
}
This produces:
For key one we have 1
For key two we have 2
For key three we have 3
For key four we have 4
For key five we have 5
For key six we have 6
For key seven we have 7
After even keys removed...
For key one we have 1
For key three we have 3
For key five we have 5
For key seven we have 7
After putting them back again...
For key one we have 1
For key three we have 3
For key five we have 5
For key seven we have 7
For key two we have 2
For key four we have 4
For key six we have 6

Where can I find the source code for ActivityTestRule?

Where can I find the source code for ActivityTestRule?
You can find it at
https://android.googlesource.com/platform/frameworks/testing/+/android-support-test/rules/src/main/java/android/support/test/rule/ActivityTestRule.java
You have to use branch android-support-test instead of master on platform/frameworks/testing project in AOSP.
You can add ActivityTestRule in code, import library and CTRL+Left Click to decompile.
If you can't import this class, you need to add it to dependencies in your build.gradle
androidTestCompile 'com.android.support.test:rules:0.3'
EDIT:
My result of decompiling:
package android.support.test.rule;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.Beta;
import android.support.test.internal.util.Checks;
import android.support.test.rule.UiThreadTestRule;
import android.util.Log;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
#Beta
public class ActivityTestRule<T extends Activity> extends UiThreadTestRule {
private static final String TAG = "ActivityInstrumentationRule";
private final Class<T> mActivityClass;
private Instrumentation mInstrumentation;
private boolean mInitialTouchMode;
private boolean mLaunchActivity;
private T mActivity;
public ActivityTestRule(Class<T> activityClass) {
this(activityClass, false);
}
public ActivityTestRule(Class<T> activityClass, boolean initialTouchMode) {
this(activityClass, initialTouchMode, true);
}
public ActivityTestRule(Class<T> activityClass, boolean initialTouchMode, boolean launchActivity) {
this.mInitialTouchMode = false;
this.mLaunchActivity = false;
this.mActivityClass = activityClass;
this.mInitialTouchMode = initialTouchMode;
this.mLaunchActivity = launchActivity;
this.mInstrumentation = InstrumentationRegistry.getInstrumentation();
}
protected Intent getActivityIntent() {
return new Intent("android.intent.action.MAIN");
}
protected void beforeActivityLaunched() {
}
protected void afterActivityLaunched() {
}
protected void afterActivityFinished() {
}
public T getActivity() {
if(this.mActivity == null) {
Log.w("ActivityInstrumentationRule", "Activity wasn\'t created yet");
}
return this.mActivity;
}
public Statement apply(Statement base, Description description) {
return new ActivityTestRule.ActivityStatement(super.apply(base, description));
}
public T launchActivity(#Nullable Intent startIntent) {
this.mInstrumentation.setInTouchMode(this.mInitialTouchMode);
String targetPackage = this.mInstrumentation.getTargetContext().getPackageName();
if(null == startIntent) {
startIntent = this.getActivityIntent();
if(null == startIntent) {
Log.w("ActivityInstrumentationRule", "getActivityIntent() returned null using default: Intent(Intent.ACTION_MAIN)");
startIntent = new Intent("android.intent.action.MAIN");
}
}
startIntent.setClassName(targetPackage, this.mActivityClass.getName());
startIntent.addFlags(268435456);
Log.d("ActivityInstrumentationRule", String.format("Launching activity %s", new Object[]{this.mActivityClass.getName()}));
this.beforeActivityLaunched();
this.mActivity = (Activity)this.mActivityClass.cast(this.mInstrumentation.startActivitySync(startIntent));
this.mInstrumentation.waitForIdleSync();
this.afterActivityLaunched();
return this.mActivity;
}
void setInstrumentation(Instrumentation instrumentation) {
this.mInstrumentation = (Instrumentation)Checks.checkNotNull(instrumentation, "instrumentation cannot be null!");
}
void finishActivity() {
if(this.mActivity != null) {
this.mActivity.finish();
this.mActivity = null;
}
}
private class ActivityStatement extends Statement {
private final Statement mBase;
public ActivityStatement(Statement base) {
this.mBase = base;
}
public void evaluate() throws Throwable {
try {
if(ActivityTestRule.this.mLaunchActivity) {
ActivityTestRule.this.mActivity = ActivityTestRule.this.launchActivity(ActivityTestRule.this.getActivityIntent());
}
this.mBase.evaluate();
} finally {
ActivityTestRule.this.finishActivity();
ActivityTestRule.this.afterActivityFinished();
}
}
}
}

Dispatchevent not working in native extension for ios

I've build a native extension for my project to fire two event about login and logout,this is code (NdPlatformExtension.as), the extIntance for singleton have tried but get same result,so ignore it.
private static const API_LOGIN_CALL:String = "LogIn";
private static const API_LOGOUT_CALL:String = "LogOut";
private static const EXTENSION_ID:String = "NdPlatformExt";
public var extContext:ExtensionContext;
private static var extIntance:NdPlatformExtension = null;
public function NdPlatformExtension()
{
extContext = ExtensionContext.createExtensionContext(EXTENSION_ID,"");
extContext.addEventListener(StatusEvent.STATUS,onEvent);
}
public static function get ExtInstance():NdPlatformExtension {
if(extIntance == null) {
extIntance = new NdPlatformExtension();
}
return extIntance;
}
public function onEvent(e:StatusEvent):void {
ConsoleLog(e.code);
switch(e.code)
{
case NdPlatformEvent.EVENT_API_LOGIN_RESULT:
{
ConsoleLog("event dispatch start");
var _succed:Boolean = dispatchEvent(new NdPlatformEvent(NdPlatformEvent.EVENT_API_LOGIN_RESULT,e.level.split('|')));
_succed = dispatchEvent(new NdPlatformEvent(NdPlatformEvent.EVENT_API_TEST,null));
ConsoleLog(_succed);//i've been checked on the log the _succed is true
}
break;
default:
{
dispatchEvent(new NdPlatformEvent(NdPlatformEvent.EVENT_API_LOGIN_RESULT,null));
}
break;
}
}
public function LogIn():String {
var _result:String = "";
try
{
_result = extContext.call(API_LOGIN_CALL) as String;
}
catch(_e:Error)
{
_result = _e.message;
}
return _result;
}
public function LogOut():String {
var _result:String = "";
try
{
_result = extContext.call(API_LOGOUT_CALL) as String;
}
catch(_e:Error)
{
_result = _e.message;
}
return _result;
}
}
}
And build a test flex project to listen the custom events, but all listeners I added are not fire. Here the view file content,the listener will add in view creationComplete.
...creationComplete="init()" title="Test">
<fx:Script>
<![CDATA[
import NdPlatformExt.NdPlatformEvent;
import NdPlatformExt.NdPlatformExtension;
import flash.events.Event;
private var ext:NdPlatformExtension = new NdPlatformExtension();
protected function button1_clickHandler(event:MouseEvent):void
{
ext.LogIn();
}
protected function button2_clickHandler(event:MouseEvent):void
{
}
public function init():void
{
ext.addEventListener(NdPlatformEvent.EVENT_API_LOGIN_RESULT,onLoginResult,true);
ext.addEventListener(NdPlatformEvent.EVENT_API_TEST,onTest,true);
}
public function onLoginResult(_event:NdPlatformEvent):void{
ext.ConsoleLog("event receive ok");
ext.LogOut();
}
public function onTest():void{
ext.ConsoleLog("test receive ok");
}
]]>
</fx:Script>
<fx:Declarations>
</fx:Declarations>
<s:Label id="output" x="14" y="70" width="300" height="32" text=""/>
<s:Button x="44" y="19" width="231" label="Login"
click="button1_clickHandler(event)" alignmentBaseline="ideographicCenter"/>
<s:Button x="44" y="111" width="231" label="Purchase"
click="button2_clickHandler(event)" alignmentBaseline="ideographicCenter"/>
but all listener i added are not fire.

Resources